package com.yss.sofa.report.engine.util;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.font.TextAttribute;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.AttributedString;
import java.text.SimpleDateFormat;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.imageio.ImageIO;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FontUnderline;
import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Header;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.extensions.XSSFCellBorder.BorderSide;

import com.yss.sofa.report.engine.Const;
import com.yss.sofa.report.engine.Report;
import com.yss.sofa.report.engine.cell.page.PageCell;
import com.yss.sofa.report.engine.cell.visible.result.ChartResultCell;
import com.yss.sofa.report.engine.cell.visible.result.FloatingResultCell;
import com.yss.sofa.report.engine.cell.visible.result.ResultCell;
import com.yss.sofa.report.engine.util.ImageUtil.PictureTextInfo.Chunk;

import net.coobird.thumbnailator.Thumbnails;

public final class ImageUtil {
	private ImageUtil(){}
	public static byte[] zoom(byte[] png, float zoom) throws Exception{
		if(zoom==1.0f){
			return png;
		}
		ByteArrayInputStream in=new ByteArrayInputStream(png);
		ByteArrayOutputStream out=new ByteArrayOutputStream();
		try {
			Thumbnails.of(in).scale(zoom).outputFormat("png").toOutputStream(out);
			return out.toByteArray();
		} finally {
			in.close();
			out.close();
		}
	}
	
	//==============================
	// Report to picture
	//==============================
	/**
	 * 将report对象转换为图片字节数组. 如果有多张报表, 则导出report.main
	 * 
	 * @param report
	 *            report对象
	 * @return byte[]
	 * @throws Exception
	 */
	public static byte[] toPng(Report report) throws Exception {

		return toPng(report, report.getSheetMapping().get(Const.MAIN));
	}
	
	/**
	 * 将report对象转换为图片字节数组. 
	 * @param report
	 *            report对象
	 * @param sheet
	 *            报表名字
	 * @return byte[]
	 * @throws Exception
	 */
	public static byte[] toPng(Report report, String sheet) throws Exception {

		if (!isValidReport(report)) {
			return new byte[0];
		}

		ByteArrayOutputStream stream = new ByteArrayOutputStream();

		export(report, stream, sheet);

		return stream.toByteArray();
	}

	/**
	 * 将report对象转换为图片字节数组. 如果有多张报表, 则导出report.main
	 * 
	 * @param report
	 *            report对象
	 * @param filename
	 *            文件名(可包含路径)
	 * @throws Exception
	 */
	public static void export(Report report, String filename) throws Exception {

		export(report, filename, report.getSheetMapping().get(Const.MAIN));
	}
	
	/**
	 * 将report对象转换为图片字节数组. 如果有多张报表, 则导出report.main
	 * 
	 * @param report
	 *            report对象
	 * @param filename
	 *            文件名(可包含路径)
	 * @throws Exception
	 */
	public static void export(Report report, OutputStream out)throws Exception {
		
		export(report, out, report.getSheetMapping().get(Const.MAIN));
	}
	
	/**
	 * 将report对象转换为图片字节数组.
	 * 
	 * @param report
	 *            report对象
	 * @param filename
	 *            文件名(可包含路径)
	 * @param sheet
	 *            报表名字
	 * @throws Exception
	 */
	public static void export(Report report, String filename, String sheet) throws Exception {

		FileOutputStream stream = new FileOutputStream(new File(filename));

		export(report, stream, sheet);
	}
	
	/**
	 * 将report对象转换为图片字节数组导出到指定输出流中.
	 * 
	 * @param report
	 *            report对象
	 * @param out
	 *            输出流
	 * @param inputSheet
	 *            指定的sheet.如果为null,则使用report.main
	 * @throws Exception
	 */
	public static void exportSingle(Report report, OutputStream out, Sheet inputSheet) throws Exception {
		if (!isValidReport(report)) {
			//throw new IllegalArgumentException("Report object format error.");
			// A4(595x842 pt) top/left(72x67 px) pt(595, 842, 54, 50.25f, 54, 50.25f)
			PictureWriter writer = new PictureWriter(793, 1123, 72, 67, 72, 67);
			writeErrorTip(writer, "This is a invalid document.", null);
			writer.save(out, PictureType.PNG);
			return;
		}
		
		Workbook excel = report.getExcel(false); //toExcel(copy(report.getMiddle()));

		ExcelWorkbook workbook = new ExcelWorkbook(excel);
		
		Sheet sheet = inputSheet != null ? inputSheet : workbook.getSheet(report.getSheetMapping().get(Const.MAIN));
		
		WriterWrapper writerWrapper = getPictureDocumentBySheetPrintSetup(sheet);
		
		PicturePHFEvent event = getPicturePHFEvent(report, sheet, writerWrapper);
		
		try {
			String reportName = report.getSheetMapping().get(Const.MAIN);
			if (report.getRowCount(reportName) > 0 && report.getColCount(reportName) > 0) {
				// 自定义绘制
				new ReportExcelPictureTable(report, workbook, writerWrapper).drawPicture(new NoSplitStrategy());
			}
			
			if (event != null) {
				event.createPHF(writerWrapper);
			}
		} catch (Exception e) {
			e.printStackTrace();
			
			writeErrorTip(writerWrapper.writer, "Appears wrong when document drawing process.", e);
		}

		writerWrapper.writer.save(out,PictureType.PNG);

	}
	
	/**
	 * 将report对象转换为图片字节导出到指定输出流中.(支持多张报表合并输出)
	 * 
	 * @param report
	 *            report对象
	 * @param out
	 *            输出流
	 * @throws Exception
	 */
	public static void export(Report report, OutputStream out, String sheetName) throws Exception {
		List<Report> reportList = getExportReportList(report, sheetName);
		Report r = (reportList != null && reportList.size() == 1) ? reportList.get(0) : null;
		Sheet sheet = null;
		if (r != null) {
			Workbook excel = report.getExcel(false);
			sheet = excel.getSheet(report.getSheetMapping().get(Const.MAIN));
		}
		exportSingle(r, out, sheet);
	}
	
	@SuppressWarnings("unchecked")
	private static List<Report> getExportReportList(Report report, String sheetName) throws Exception {
		if (!isValidReport(report)) {
			return null;
		}
		
		Workbook excel = report.getExcel(false);
		
		Constructor<Report> constructor = Report.class.getDeclaredConstructor(new Class[0]);
		constructor.setAccessible(true);
		Field middleField = Report.class.getDeclaredField("middle");
		middleField.setAccessible(true);
		Field outputField = Report.class.getDeclaredField("output");
		outputField.setAccessible(true);
		Field matrixsField = Report.class.getDeclaredField("matrixs");
		matrixsField.setAccessible(true);
		Field imagesField = Report.class.getDeclaredField("images");
		imagesField.setAccessible(true);
		Field excelField = Report.class.getDeclaredField("excel");
		excelField.setAccessible(true);
		
		List<Report> reportList = new ArrayList<Report>();
		
		for (Iterator<String> iter = report.getSheetMapping().keySet().iterator(); iter.hasNext(); ) {
			String templateName = iter.next(); // main, main.a, main.b
			if (Const.FORM.equalsIgnoreCase(templateName) || Const.STYLE.equalsIgnoreCase(templateName)) {
				continue;
			}
			String reportName = report.getSheetMapping().get(templateName);

			if (!reportName.equals(sheetName)) {
				continue;
			}
			
			Report r = constructor.newInstance(new Object[0]);
			
			r.getDatas().putAll(report.getDatas());
			r.getParams().putAll(report.getParams());
			r.getRawParams().putAll(report.getRawParams());
			r.getConsts().putAll(report.getConsts());
			
			PageCell pageCell = report.getPage(reportName);
			r.add(pageCell, report.getCells(reportName));
			r.getSheetMapping().put(Const.MAIN, reportName);
			
			outputField.set(r, reportName/*Const.MAIN*/);
			middleField.set(r, report.getMiddle());
			matrixsField.set(r, matrixsField.get(report));
			excelField.set(r, excel);
			
			Map<ResultCell, byte[]> images = report.getImages(reportName);
			Map<String, Map<ResultCell,byte[]>> toImages = (Map<String, Map<ResultCell,byte[]>>) imagesField.get(r);
			if (images == null || images.size() == 0) {
				images = new HashMap<ResultCell, byte[]>(0);
			}
			toImages.put(Const.MAIN, images);
			toImages.put(reportName, images);
			
			reportList.add(r);
		}
		
		return reportList;
	}
	
	/**
	 * 设置页眉/页脚. (如果包含页眉页脚，则页边距取页眉页脚边距，页眉页脚高度是页边距-页眉页脚边距)<br/>
	 * 页眉/页脚按Excel中页眉/页脚设置, 分左中右3个部分, 左边对齐方式top-left, 中间是top-center, 右边是top-right<br/>
	 * Excel打印预览中页边距中的居中方式不处理, 页眉页脚都是左中右3个部分, 奇偶页/首页不同不处理.<br/>
	 * Picture页眉页脚在xml的sectPr中设置引用, 不必像PDF处理总页数那样麻烦, 最后设置即可.
	 * 
	 * @param report
	 * @param sheet
	 * @param writer
	 * @see com.yss.sofa.report.engine.Engine#generateMiddle
	 * @see com.yss.sofa.report.engine.util.PrintUtil#processPlaceholder 不需要解析vars, 传入的值都是处理好的. 日期时间统一参考
	 * @see 参考文档附录A-page单元格
	 */
	private static PicturePHFEvent getPicturePHFEvent(Report report, Sheet sheet, WriterWrapper writerWrapper) {
		
		if(!hasPHF(report, sheet)) {
			return null;
		}
		
		// 如果包含页眉页脚，则页边距取页眉页脚边距，页眉页脚高度是页边距-页眉页脚边距
		// Gets the size of the margin in inches. Needs covert to Px
		double fac = UNIT_INCH_TO_PX_FAC;
		int headerHeight = (int) Math.floor((sheet.getMargin(Sheet.TopMargin) - sheet.getMargin(Sheet.HeaderMargin)) * fac);
		int footerHeight = (int) Math.floor((sheet.getMargin(Sheet.BottomMargin) - sheet.getMargin(Sheet.FooterMargin)) * fac);
		
		// 页眉是3个格子, 页脚也是3个格子, 且只处理文本内容(Excel可以设置图片等其他内容)
		List<PictureCell> header = new ArrayList<PictureCell>();
		List<PictureCell> footer = new ArrayList<PictureCell>();
		
		PicturePHFEvent event = new PicturePHFEvent();
		event.headerHeight = headerHeight;
		event.footerHeight = footerHeight;
		
		// 变量值&D,&T,&F,&A
		Date now = new Date();
		event.dateConstString = new SimpleDateFormat("yyyy-MM-dd").format(now);
		event.timeConstString = new SimpleDateFormat("HH:mm:ss").format(now);
		event.fileNameConstString = sheet.getSheetName();
		event.sheetNameConstString = sheet.getSheetName();
		
		// Excel默认字体 - Excel默认是"等线 Light 11"
		if(sheet instanceof XSSFSheet){
			XSSFFont defaultFont = ((XSSFSheet)sheet).getWorkbook().getFontAt((short) 0);
			PictureFont firstFont = ReportExcelPictureTable.getFontByExcel(defaultFont);
			event.font = firstFont;
		}else if(sheet instanceof HSSFSheet){
			HSSFFont defaultFont =((HSSFSheet)sheet).getWorkbook().getFontAt((short)0);
			PictureFont firstFont = ReportExcelPictureTable.getFontByExcel03(defaultFont,sheet);
			event.font = firstFont;
		}

		
		// 页眉左边部分
		String reportName = report.getSheetMapping().get(Const.MAIN);
		Header pageHeader = sheet.getHeader();
		String hleft = pageHeader.getLeft();
		try {
			PictureCell cell = getPHFCell(event, hleft, 1, 1, headerHeight, HVAlignment.LEFT, HVAlignment.TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]左侧页眉：" + hleft, e);
		}
		
		// 页眉中间部分
		String hcenter = pageHeader.getCenter();
		try {
			PictureCell cell = getPHFCell(event, hcenter, 1, 1, headerHeight, HVAlignment.CENTER, HVAlignment.TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]中间页眉："+ hcenter, e);
		}
		
		// 页眉右边部分
		String hright = pageHeader.getRight();
		try {
			PictureCell cell = getPHFCell(event, hright, 1, 1, headerHeight, HVAlignment.RIGHT, HVAlignment.TOP);
			header.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]右侧页眉："+ hright, e);
		}
		
		// 页脚左边部分
		Footer pageFooter = sheet.getFooter();
		String fleft = pageFooter.getLeft();
		try {
			PictureCell cell = getPHFCell(event, fleft, 1, 1, footerHeight, HVAlignment.LEFT, HVAlignment.TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]左侧页脚：" + fleft, e);
		}
		
		// 页脚中间部分
		String fcenter = pageFooter.getCenter();
		try {
			PictureCell cell = getPHFCell(event, fcenter, 1, 1, footerHeight, HVAlignment.CENTER, HVAlignment.TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]中间页脚："+ fcenter, e);
		}
		
		// 页脚右边部分
		String fright = pageFooter.getRight();
		try {
			PictureCell cell = getPHFCell(event, fright, 1, 1, footerHeight, HVAlignment.RIGHT, HVAlignment.TOP);
			footer.add(cell);
		} catch (Exception e) {
			throw new IllegalArgumentException("无法预处理sheet[" + reportName + "]右侧页脚："+ fright, e);
		}

		event.header = header;
		event.footer = footer;

		return event;
	}
	
	private static PictureCell getPHFCell(PicturePHFEvent event, String text, int rowspan, int colspan, int height, HVAlignment ha, HVAlignment va) {
		
		text = event.renderConstString(text);

		PictureCell cell = new PictureCell();
		cell.cellText = text;
		cell.horizontalAlignment = ha;
		cell.verticalAlignment = va;
		
		cell.cellFont = event.font;
		//cell.fontSelector = event.fontSelector;
		
		return cell;
	}
	
	private static boolean hasPHF(Report report, Sheet sheet) {
		PageCell page = report.getPage(report.getSheetMapping().get(Const.MAIN));
		
		if (page.isIncludePHF()) {
			return true;
		}
		
		// sheet获得的不处理,只判断传入的参数. hasPHF(sheet.getHeader())
		
		return false;
	}
	
	/**
	 * 通过Excel中Sheet的打印设置获得Picture的文档大小.<br/>
	 * printSetup.getPaperSize()可以获得更多的值.<br/>
	 * https://msdn.microsoft.com/zh-cn/library/ms226507 <br/>
	 * http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/PrintSetup.html <br/>
	 * http://poi.apache.org/apidocs/org/apache/poi/xssf/usermodel/XSSFSheet.html <br/>
	 * 
	 * @see org.docx4j.model.structure.PageDimensions#setPgSize
	 * @param sheet
	 * @return
	 */
	private static WriterWrapper getPictureDocumentBySheetPrintSetup(Sheet sheet) {
		// 只需要绘制一张完整的图片, 图片大小需要动态计算得到, 所以这里返回一个简单的writer, 之后获得了图的实际高宽后再构造有效的writer
		//return new PictureWriter(1, 1, 0, 0, 0, 0); // new PictureWriter(793, 1123, 72, 67, 72, 67);
		
		// Gets the size of the margin in inches. Needs covert to px.
		double fac = UNIT_INCH_TO_PX_FAC; // 1=96px
		int marginLeft = (int) Math.floor(sheet.getMargin(Sheet.LeftMargin) * fac);
		int marginRight = (int) Math.floor(sheet.getMargin(Sheet.RightMargin) * fac);
		int marginTop = (int) Math.floor(sheet.getMargin(Sheet.TopMargin) * fac);
		int marginBottom = (int) Math.floor(sheet.getMargin(Sheet.BottomMargin) * fac);
		
		PictureWriter writer = new PictureWriter(1, 1, marginTop, marginLeft, marginBottom, marginRight);
		
		return new WriterWrapper(writer);
	}
	
	/**
	 * <code>ReportExcelPictureTable</code>绘制Picture的具体实现类.
	 */
	private static class ReportExcelPictureTable {
		
		private Report report;
		private ExcelWorkbook workbook;
		private PictureWriter writer;
		private WriterWrapper writerWrapper;
		
		public ReportExcelPictureTable(Report report, ExcelWorkbook workbook, WriterWrapper writerWrapper) {
			this.report = report;
			this.workbook = workbook;
			this.writer = writerWrapper.writer;
			this.writerWrapper = writerWrapper;
		}
		
		/**
		 * 按照report绘制Picture表格. <br/>
		 * <blockquote><pre>
		 * HTML绘制标签顺序: css -> js -> &lt;table&gt; -> title.tr -> header.tr -> detail.tr -> footer.tr -> floatings ==> 最后结构 CSS + TABLE + floatings + JS
		 * Picture绘制表格顺序:  title -> header -> detail -> footer -> floatings ==> 最后结构 TABLE + floatings
		 * </pre></blockquote>
		 * 
		 * @param strategy
		 * @return
		 * @throws Exception
		 */
		public void drawPicture(DrawStrategy strategy) throws Exception {
			strategy.setReportExcelPictureTable(this);
			
			PictureTableCellInfo cellInfo = getPictureTableCellInfo(strategy);
			
			updateWriter(strategy, cellInfo);
			
			draw(strategy, cellInfo);
		}
		
		private void draw(DrawStrategy strategy, PictureTableCellInfo cellInfo) throws Exception {
			PicturePageInfo pageInfo = new PicturePageInfo(writer, strategy.getDrawBodySplitHeight());
			
			List<PictureCell> splitColMapping = cellInfo.cells;
			FloatingCollection fc = new FloatingCollection();
			fc.addFloating(cellInfo.floating);
			fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
			
			drawCells(pageInfo, cellInfo, splitColMapping, fc);
		}
		
		private void updateWriter(DrawStrategy strategy, PictureTableCellInfo cellInfo) {
			if (strategy instanceof NoSplitStrategy) {
				int actulPageWidth = writer.leftMargin() + cellInfo.totalWidth + writer.rightMargin() + 1;
				int actulPageHeight = writer.topMargin() + (int)((NoSplitStrategy) strategy).cellMaxHeight + writer.bottomMargin() + 1;
				PictureWriter newWriter = new PictureWriter(actulPageWidth, actulPageHeight, 
				                                            writer.topMargin(), writer.leftMargin(), 
				                                            writer.bottomMargin(), writer.rightMargin());
				this.writer.close();
				this.writer = newWriter;
				this.writerWrapper.writer = newWriter;
			}
		}
		
		private void drawCells(PicturePageInfo pageInfo, PictureTableCellInfo cellInfo,
							   List<PictureCell> cells, FloatingCollection fc) throws Exception {
			if (cells == null || cells.size() == 0 
					|| pageInfo.pageHeight < 1 || pageInfo.pageFitHeight < 1 
					|| pageInfo.pageWidth < 1 || pageInfo.pageFitWidth < 1) {
				return;
			}
			
			boolean isSplitRow = false;
			boolean isSplitCol = false;

			List<PictureCell> splitColMapping = new ArrayList<PictureCell>();
			List<PictureCell> splitRowMapping = new ArrayList<PictureCell>();
			Map<Integer, List<PictureCell>> splitRowColMapping = new HashMap<Integer, List<PictureCell>>();
			splitRowColMapping.put(Integer.valueOf(1), splitRowMapping);
			

			// 要求cells中的cell是按rowIndex和colIndex顺序添加
			List<List<PictureCell>> rows = new ArrayList<List<PictureCell>>();
			List<PictureCell> row_ = new ArrayList<PictureCell>();
			rows.add(row_);
			for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = cells.get(0).rowIndex; cellIndex < cellSize; cellIndex++) {
				PictureCell xcell = cells.get(cellIndex);
				if (currentRowIndex == xcell.rowIndex) {
					row_.add(xcell); // 找出一行的全部格子, 按行为单位绘制
				} else {
					currentRowIndex = xcell.rowIndex;
					--cellIndex;
					row_ = new ArrayList<PictureCell>();
					rows.add(row_);
				}
			}
			
			Integer rightStartPageWidthNum = Integer.valueOf(2);
			int mistake = MISTAKE;
			
			for (List<PictureCell> row : rows) {
				
				PictureCell firstCell = row.get(0);
				float firstFitYPos = pageInfo.transferToFitYPos(firstCell.yPos);
				while (firstFitYPos >= pageInfo.getActulPageMaxFitHeight()) {
					 //绘制当页floating
					if (fc.hasFloating()) {
						drawFloating(pageInfo, cellInfo, fc, false);
					}
					
					if (splitColMapping.size() > 0) {
						drawSplitColCells(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
					} else {
						// 可能有表头或其他切分
						drawSplitColCellsForHeader(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitRowColMapping);
					}
					
					pageInfo.newPage();
					
					// 表头必须在页面开始画
					processCellHeader(pageInfo, cellInfo, fc, splitRowColMapping);
					
					if (splitRowMapping.size() > 0) {
						writeSplitRowCells(pageInfo.pageHeightNum, pageInfo, cellInfo, splitRowMapping);
					}
				}
				
				Integer pageNum = pageInfo.pageHeightNum;
				
				PicturePageInfo currentPageInfo = pageInfo.getPageInfo(pageNum);
				
				float pageFitHeight = currentPageInfo.getActulPageFitHeight();
				
				for (PictureCell cell : row) {
					float cellWidth = cell.drawWidth;
					float cellHeight = cell.drawHeight;
					float xPos = cell.xPos;
					float yPos = cell.yPos;

					// 注意: Picture绘制(0, 0)位于页面左上角, PDF绘制(0, 0)位于页面左下角
					float drawXPos = pageInfo.transferToPageXPos(pageNum, xPos);

					if (drawXPos < 0) {
						if (drawXPos < -1) {
							continue;
						}
						// 在完全切分的范围内,这些格子最后新建一页再画
						splitColMapping.add(cell);
					} else {
						float drawYPos = pageInfo.transferToPageYPos(pageNum, yPos);
						float fitYPos = pageInfo.transferToFitYPos(yPos);
						isSplitRow = false;
						isSplitCol = false;

						if (fitYPos + cellHeight > pageFitHeight + mistake) {
							// 存在行切分
							isSplitRow = true;
							splitRowMapping.add(cell);
							cell.nextSplitPageNum = pageNum.intValue() + 1;
						}

						if (drawXPos + cellWidth > pageInfo.pageFitWidth + mistake) {
							// 存在列切分
							isSplitCol = true;
							splitColMapping.add(cell);
						}
						
						writeCell(cell, drawXPos, drawYPos, fitYPos, pageInfo, cellInfo, isSplitRow, isSplitCol);

						if (isSplitRow && isSplitCol) {
							cell.rowSplitDrawXPos = drawXPos;
							cell.rowSplitDrawYPos = pageInfo.topMargin;
							
							float drawWidth = pageInfo.pageFitWidth - drawXPos;
							cell.drawCompleteWidth += drawWidth;
							float lastDrawWidth = cell.drawWidth - drawWidth;
							cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
							cell.drawWidth = lastDrawWidth;
							
							cell.drawSplitWidth = drawWidth;

							cell.drawHeight = fitYPos + cellHeight - pageFitHeight;
							cell.drawSplitHeight = cell.drawHeight;
							cell.drawCompleteHeight = cell.maxHeight - cell.drawHeight;
							cell.drawCompletePreHeight = pageFitHeight - fitYPos;
							
							cell.hasRowSplit = true;
							cell.hasColSplit = true;
							cell.hasRowColSplit = true;
						} else {					
	    					if (isSplitRow) {
	    						cell.rowSplitDrawXPos = drawXPos;
	    						cell.rowSplitDrawYPos = pageInfo.topMargin;

	    						cell.drawHeight = fitYPos + cellHeight - pageFitHeight;
	    						cell.drawSplitHeight = cell.drawHeight;
	    						cell.drawCompleteHeight = cell.maxHeight - cell.drawHeight;
	    						cell.drawCompletePreHeight = pageFitHeight - fitYPos;
	    						
	    						cell.hasRowSplit = true;
	    					} else if (isSplitCol) {
	    						float drawWidth = pageInfo.pageFitWidth - drawXPos;
	    						cell.drawCompleteWidth += drawWidth;
	    						float lastDrawWidth = cell.drawWidth - drawWidth;
	    						cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
	    						cell.colSplitDrawXPos = pageInfo.leftMargin;
	    						cell.drawWidth = lastDrawWidth;
	    						cell.hasColSplit = true;
	    					}
						}
					}
				}
			}

			// 绘制当页floating
			if (fc.hasFloating()) {
				drawFloating(pageInfo, cellInfo, fc, false);
			}

			drawSplitColCells(rightStartPageWidthNum, pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
			
			// 绘制最后的行切
			drawLastSplitRowCells(pageInfo, cellInfo, fc, splitRowColMapping);
			
			// 如果还有浮动对象,则将其剩下部分绘制完
			if (fc.hasFloating()) {
				drawBottomFloating(pageInfo, cellInfo, fc);
			}
			
		}
		
		private void drawLastSplitRowCells(PicturePageInfo originalPageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc, 
		                                   Map<Integer, List<PictureCell>> splitRowColMapping) throws Exception {
			Integer startPageWidthNum = Integer.valueOf(1);
			boolean flag = hasDrawSplitColCellsForSplitRow(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
			while (flag) {
				// 表头必须在页面开始画
				if (fc.hasFloating()) {
					drawFloating(originalPageInfo, cellInfo, fc, false);
				}
				originalPageInfo.newPage();
				processCellHeader(originalPageInfo, cellInfo, fc, splitRowColMapping);
				drawSplitColCellsForHeader(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
				flag = hasDrawSplitColCellsForSplitRow(startPageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
			}
		}
		
		private void processCellHeader(PicturePageInfo pageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc, Map<Integer, List<PictureCell>> splitRowColMapping) {
			// 表头必须在页面开始画
			if (cellInfo.headerInfo != null) {
				List<PictureCell> headerCells = cellInfo.headerInfo.pageNumHeaderMapping.remove(pageInfo.pageHeightNum);
				if (headerCells == null || headerCells.size() == 0) {
					return;
				}
				
				// 改变行切与表头连接格子的YPos
				int offset = cellInfo.headerInfo.headerHeight;
				int pageWidthNumInt = 1;
				int pageHeightNumInt = pageInfo.pageHeightNum;
				do {
					Integer nextPageWidthNum = Integer.valueOf(pageWidthNumInt);
					List<PictureCell> splitRow = splitRowColMapping.get(nextPageWidthNum);
					if (splitRow != null) {
						if (splitRow.size() > 0) {
							for (PictureCell xcell : splitRow) {
								if (xcell.drawCompleteHeight > 0) {
									xcell.rowSplitDrawYPos += offset;
									xcell.drawSplitHeightOffset = offset;
									xcell.nextSplitPageNum = pageHeightNumInt;
									
									boolean isStartXPos = (xcell.xPos == 0 || (xcell.originalXPos + xcell.drawCompleteWidthXPos) % pageInfo.onePageFitWidth == 0) ? true : false;
									if (isStartXPos && pageWidthNumInt > 1) {
										xcell.drawCompleteWidth = xcell.drawCompleteWidthXPos;
									} else {
										xcell.drawCompleteWidth = 0;
									}
								}
							}
							List<PictureCell> cells = getSplitRowCellHeader(pageInfo, pageHeightNumInt, nextPageWidthNum, headerCells);
							if (cells.size() > 0) {
								splitRow.addAll(0, cells);
							}
						}
					} else {
						break;
					}
					++pageWidthNumInt;
				} while (true);
			}
		}

		// 需要保证表头高度不能跨页
		private List<PictureCell> getSplitRowCellHeader(PicturePageInfo pageInfo, int nextSplitPageNum, int pageNum, List<PictureCell> headerCells) {
			List<PictureCell> result = new ArrayList<PictureCell>();
			float pageStartXPos = pageInfo.onePageFitWidth * (pageNum - 1);
			float pageEndXPos = pageInfo.onePageFitWidth * pageNum;
			for (PictureCell xcell : headerCells) {
				float xPos = xcell.xPos;
				float cellWidth = xcell.maxWidth;
				if (xPos >= pageStartXPos && xPos < pageEndXPos) {
					xcell = PictureCell.copyCell(xcell);
					result.add(xcell);
					
					if (xPos + cellWidth > pageEndXPos) {
						xcell.drawSplitWidth = pageEndXPos - xPos;
						xcell.drawWidth = xcell.drawSplitWidth;
						xcell.hasColSplit = true;
					} else {
						xcell.drawSplitWidth = cellWidth;
					}
					
					xcell.drawSplitHeight = xcell.maxHeight;
					xcell.rowSplitDrawXPos = pageInfo.leftMargin + (xPos % pageInfo.onePageFitWidth);
					xcell.rowSplitDrawYPos = pageInfo.transferToPageYPos(nextSplitPageNum, xcell.yPos);
					xcell.isHeader = true;
					xcell.nextSplitPageNum = nextSplitPageNum;
					
				} else if (xPos < pageStartXPos && xPos + cellWidth > pageStartXPos) {
					xcell = PictureCell.copyCell(xcell);
					result.add(xcell);

					xcell.drawSplitWidth = (xPos + cellWidth) - pageStartXPos;
					if (xcell.drawSplitWidth > pageInfo.onePageFitWidth) {
						xcell.drawSplitWidth = pageInfo.onePageFitWidth;
					}
					xcell.drawWidth = xcell.drawSplitWidth;

					xcell.drawSplitHeight = xcell.maxHeight;
					xcell.rowSplitDrawXPos = pageInfo.leftMargin;
					xcell.rowSplitDrawYPos = pageInfo.transferToPageYPos(nextSplitPageNum, xcell.yPos);
					xcell.hasColSplit = true;
					xcell.isHeader = true;
					xcell.nextSplitPageNum = nextSplitPageNum;
					
					xcell.drawCompleteWidth = pageStartXPos - xPos;
					xcell.drawCompleteWidthXPos = xcell.drawCompleteWidth;
				}
			}
			return result;
		}

		/**
		 * 绘制下方浮动对象(先画行再画列时将超过表格下边界的浮动对象绘制完毕)
		 * @param pageInfo
		 * @param cellInfo
		 * @param fc
		 */
		private void drawBottomFloating(PicturePageInfo pageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc) {
			if (!fc.hasFloating()) { // 如果完整页面后应该有空白页, 这里将其去掉
				return;
			}
			
			if (fc.maxWH == null) {
				fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
			}
			
			int w = fc.maxWH[0];
			int h = fc.maxWH[1];
			if (w == 0 && h == 0) {
				return;
			}
			
			// 先画行保证当前页已经画完, 所以只有当h > pageInfo.pageHeightSplitNum时再画
			if (h > pageInfo.pageHeightSplitNum) {
				float pageWidth = pageInfo.onePageFitWidth;
				float pageHeight = pageInfo.onePageFitHeight;
				for (int heightNum = pageInfo.pageHeightSplitNum; heightNum <= h; ++heightNum) {
					PicturePageInfo pageInfoForRow = pageInfo.newPage();
					
					List<Floating> floatingList = fc.getFloating(pageInfoForRow.pageHeightSplitNum, 1, pageWidth, pageHeight);
					if (floatingList != null && floatingList.size() > 0) {
						writeFloating(pageInfoForRow, cellInfo, fc, floatingList);
					}
					if (w > 1) {
						int pageHeightSplitNum = pageInfoForRow.pageHeightSplitNum;
						for (int widthNum = 2; widthNum <= w; ++widthNum) {
							pageInfoForRow = pageInfoForRow.newPageForHorizontal(Integer.valueOf(widthNum));
							floatingList = fc.getFloating(pageHeightSplitNum, widthNum, pageWidth, pageHeight);
							if (floatingList != null && floatingList.size() > 0) {
								writeFloating(pageInfoForRow, cellInfo, fc, floatingList);
							}
							
							if (!fc.hasFloating()) {
								return; // 后面空白页废弃
							}
						}
					}
					
					if (!fc.hasFloating()) {
						return; // 后面空白页废弃
					}
				}
			}
		}

		/**
		 * 绘制浮动对象
		 * @param pageInfo
		 * @param cellInfo
		 * @param fc
		 * @param drawAll
		 */
		private void drawFloating(PicturePageInfo pageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc, boolean drawAll) {
			if (!fc.hasFloating()) {
				return;
			}
			
			if (drawAll) {
				if (fc.maxWH == null) {
					fc.maxWH = fc.getFloatingMaxWH(pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
				}
				
				int w = fc.maxWH[0];
				int h = fc.maxWH[1];
				if (w == 0 && h == 0) {
					return;
				}
				
				if (w > pageInfo.pageWidthNum) {
					
					pageInfo.pageWidthNum++;
					
					float pageWidth = pageInfo.onePageFitWidth;
					float pageHeight = pageInfo.onePageFitHeight;
					for (int widthNum = pageInfo.pageWidthNum; widthNum <= w; widthNum++) {
						
						int pageCount = h;
						
						for (int heightNum = 1, newPageNum = 0; heightNum <= h; heightNum++) {
							++newPageNum;
							
							List<Floating> floatingList = fc.getFloating(heightNum, widthNum, pageWidth, pageHeight);


							if (floatingList != null && floatingList.size() > 0) {
								for (int i = 0; i < newPageNum; i++) {
									pageInfo.newPage();
									--pageCount;
								}
								writeFloating(pageInfo, cellInfo, fc, floatingList);
								newPageNum = 0;
								
								if (!fc.hasFloating()) {
									break; // 补全最后的空页不退出
									//return;
								}
							}
						}

						// 整列空的补全, 列后空页补全
						if (pageCount > 0) {
							for (int i = 0; i < pageCount; i++) {
								pageInfo.newPage();
							}
						}
						
						pageInfo.pageWidthNum++;
						
					} // end for widthNum

				}
			} else {
				List<Floating> floatingList = fc.getFloating(pageInfo.pageHeightSplitNum, pageInfo.pageWidthNum, pageInfo.onePageFitWidth, pageInfo.onePageFitHeight);
				writeFloating(pageInfo, cellInfo, fc, floatingList);
			}
		}
		
		private void writeFloating(PicturePageInfo pageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc, List<Floating> floatingList) {
			if (floatingList == null || floatingList.size() == 0) {
				return;
			}
			
			try {
	    		for (Floating floating : floatingList) {
	    			// 目前只处理图片
	    			PictureImage image = floating.cell.image;
	    			if (image != null && image.hasImage()) {
	    				writer.writeFloatingToPage(pageInfo, floating, cellInfo.imageZoom);
	    			}
	    		}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		private void drawSplitColCellsForHeader(Integer pageWidthNum, PicturePageInfo originalPageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc, 
		                                        Map<Integer, List<PictureCell>> splitRowColMapping) throws Exception {
			// 新建一页
			Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
			
			// 先将上一行剩余部分画完
			for (int i = pageWidthNum.intValue(), j = splitRowColMapping.size(); i <= j; ++i) {
				PicturePageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
				List<PictureCell> splitRow = splitRowColMapping.get(Integer.valueOf(i));
				if (splitRow.size() > 0) {
					writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
				}
			}
		}

		private void drawSplitColCells(Integer pageWidthNum, PicturePageInfo originalPageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc,
										   List<PictureCell> cells, Map<Integer, List<PictureCell>> splitRowColMapping) throws Exception {
			// 绘制右侧内容
			if (cells.size() > 0) {
				
				// 新建一页
				Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
				PicturePageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
				
				// 先将上一行剩余部分画完
				List<PictureCell> splitRow = splitRowColMapping.get(pageWidthNum);
				if (splitRow == null) {
					splitRow = new ArrayList<PictureCell>();
					splitRowColMapping.put(pageWidthNum, splitRow);
				}
				if (splitRow.size() > 0) {
					writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
				}
				
				List<List<PictureCell>> rows = new ArrayList<List<PictureCell>>();
				List<PictureCell> row_ = new ArrayList<PictureCell>();
				rows.add(row_);
				for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = cells.get(0).rowIndex; cellIndex < cellSize; cellIndex++) {
					PictureCell xcell = cells.get(cellIndex);
					if (currentRowIndex == xcell.rowIndex) {
						if (xcell.hasColSplit) {
							xcell = PictureCell.copyCell(xcell);
							xcell.drawCompleteWidthXPos = xcell.drawCompleteWidth;
						}
						row_.add(xcell); // 找出一行的全部格子, 按行为单位绘制
					} else {
						currentRowIndex = xcell.rowIndex;
						--cellIndex;
						row_ = new ArrayList<PictureCell>();
						rows.add(row_);
					}
				}
				
				boolean isSplitRow = false;
				boolean isSplitCol = false;
				
				List<PictureCell> splitColMapping = new ArrayList<PictureCell>();
				PictureCell xcell = null;
				boolean isRowColSplitCell = false;
				
				int mistake = MISTAKE;
				PicturePageInfo currentPageInfo = pageInfo;
				float pageFitHeight = currentPageInfo.getActulPageFitHeight();
				
				for (List<PictureCell> row : rows) {

					for (PictureCell cell : row) {
						float cellWidth = cell.drawWidth;
						float cellHeight = cell.drawHeight + cell.drawCompletePreHeight; //cell.maxHeight;
						float xPos = cell.xPos;
						float yPos = cell.yPos;

						// 注意: Picture绘制(0, 0)位于页面左上角, PDF绘制(0, 0)位于页面左下角
						float drawXPos = pageInfo.transferToPageXPos(pageNum, xPos);

						if (drawXPos < 0) {
							if (drawXPos < -1) {
								continue;
							}
							// 在完全切分的范围内,这些格子最后新建一页再画
							splitColMapping.add(cell);
						} else {
							float drawYPos = pageInfo.transferToPageYPos(pageNum, yPos);
							float fitYPos = pageInfo.transferToFitYPos(yPos);
							isSplitRow = false;
							isSplitCol = false;
							isRowColSplitCell = false;

							if (fitYPos + cellHeight > pageFitHeight + mistake) {
								// 存在行切分
								isSplitRow = true;
								
								if (cell.hasRowColSplit) {
									isRowColSplitCell = true;
								} else {
									splitRow.add(cell);
								}
								
								cell.nextSplitPageNum = pageNum.intValue() + 1;
							}

							if (drawXPos + cellWidth > pageInfo.pageFitWidth + mistake) {
								// 存在列切分
								isSplitCol = true;
								splitColMapping.add(cell);
							}
							
							float drawCompleteHeight = cell.drawCompleteHeight;
							cell.drawCompleteHeight = drawCompleteHeight - cell.drawCompletePreHeight;
							writeCell(cell, drawXPos, drawYPos, fitYPos, pageInfo, cellInfo, isSplitRow, isSplitCol);
							cell.drawCompleteHeight = drawCompleteHeight;

							if (isSplitRow && isSplitCol) {
								cell.rowSplitDrawXPos = drawXPos;
								
								float drawWidth = pageInfo.pageFitWidth - drawXPos;
								cell.drawCompleteWidth += drawWidth;
								float lastDrawWidth = cell.drawWidth - drawWidth;
								cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
								cell.drawWidth = lastDrawWidth;
								
								cell.drawSplitWidth = drawWidth;
								
								if (!cell.hasColSplit) {
									cell.rowSplitDrawYPos = pageInfo.topMargin;
    								cell.drawHeight = fitYPos + cellHeight - pageFitHeight;
    								cell.drawSplitHeight = cell.drawHeight;
    								cell.drawCompleteHeight = cell.maxHeight - cell.drawHeight;
    								cell.drawCompletePreHeight = pageFitHeight - fitYPos;
								}
								
								cell.hasRowSplit = true;
								cell.hasColSplit = true;
								cell.hasRowColSplit = true;
								
	    						if (isRowColSplitCell) {
	    							xcell = PictureCell.copyCell(cell);
	    							splitRow.add(xcell);
	    						}
							} else {					
		    					if (isSplitRow) {
		    						if (!cell.hasColSplit) {
			    						cell.rowSplitDrawXPos = drawXPos;
			    						cell.rowSplitDrawYPos = pageInfo.topMargin;
			    						
    		    						cell.drawHeight = fitYPos + cellHeight - pageFitHeight;
    		    						cell.drawSplitHeight = cell.drawHeight;
    		    						cell.drawCompleteHeight = cell.maxHeight - cell.drawHeight;
    		    						cell.drawCompletePreHeight = pageFitHeight - fitYPos;
		    						}
		    						
		    						cell.hasRowSplit = true;
		    						
		    						if (isRowColSplitCell) {
		    							xcell = PictureCell.copyCell(cell);
		    							xcell.rowSplitDrawXPos = drawXPos;
		    							xcell.drawSplitWidth = cell.maxWidth - cell.drawCompleteWidth;
		    							xcell.hasColSplit = false;
		    							splitRow.add(xcell);
		    						}
		    					} else if (isSplitCol) {
		    						float drawWidth = pageInfo.pageFitWidth - drawXPos;
		    						cell.drawCompleteWidth += drawWidth;
		    						float lastDrawWidth = cell.drawWidth - drawWidth;
		    						cell.xPos = pageInfo.onePageFitWidth * pageInfo.pageWidthNum;
		    						cell.colSplitDrawXPos = pageInfo.leftMargin;
		    						cell.drawWidth = lastDrawWidth;
		    						cell.hasColSplit = true;
		    					}
							}
						}
					}

				}
				
				// 绘制当页floating
				if (fc.hasFloating()) {
					drawFloating(pageInfo, cellInfo, fc, false);
				}
				
				if (splitColMapping.size() > 0) {
					// 递归绘制
					drawSplitColCells(Integer.valueOf(pageWidthNum.intValue() + 1), pageInfo, cellInfo, fc, splitColMapping, splitRowColMapping);
				}
				
				cells.clear();
				
			} else {
				if (hasDrawSplitColCellsForSplitRow(pageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping)) {
					drawSplitColCellsForSplitRow(pageWidthNum, originalPageInfo, cellInfo, fc, splitRowColMapping);
				}
			}
		}

		private boolean drawSplitColCellsForSplitRow(Integer pageWidthNum, PicturePageInfo originalPageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc, 
			                   		                  Map<Integer, List<PictureCell>> splitRowColMapping) throws Exception {
	   			boolean flag = false;
	   			
	   			List<PictureCell> splitRow = splitRowColMapping.get(pageWidthNum);
	   			if (splitRow == null) {
	   				return flag;
	   			}
	   			
	   			Integer pageNum = Integer.valueOf(originalPageInfo.pageHeightNum);
	   			PicturePageInfo pageInfo = originalPageInfo.newPageForHorizontal(pageNum);
	   			if (splitRow.size() > 0) {
	   				// 绘制当页floating
       				if (fc.hasFloating()) {
       					drawFloating(pageInfo, cellInfo, fc, false);
       				}
	   				
	   				writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
	   				
	   				flag = splitRow.size() > 0;
	   			}
	   			
	   			int pageWidthNumInt = pageWidthNum.intValue();
	   			do {
	   				Integer nextPageWidthNum = Integer.valueOf(++pageWidthNumInt);
	   				splitRow = splitRowColMapping.get(nextPageWidthNum);
	   				if (splitRow != null) {
	   					pageInfo = pageInfo.newPageForHorizontal(pageNum);
	   					if (splitRow.size() > 0) {
	   						// 绘制当页floating
	       					if (fc.hasFloating()) {
	       						drawFloating(pageInfo, cellInfo, fc, false);
	       					}
	   						
	   						writeSplitRowCells(pageNum, pageInfo, cellInfo, splitRow);
	   						
	   						if (!flag) {
	   							flag = true;
	   						}
	   					}
	   				} else {
	   					break;
	   				}
	   			} while (true);
	   			
	   			return flag;
			}
		
		private boolean hasDrawSplitColCellsForSplitRow(Integer pageWidthNum, PicturePageInfo originalPageInfo, PictureTableCellInfo cellInfo, FloatingCollection fc, 
			                      		                  Map<Integer, List<PictureCell>> splitRowColMapping) {
			int pageWidthNumInt = pageWidthNum.intValue();
			
			do {
				Integer nextPageWidthNum = Integer.valueOf(pageWidthNumInt++);
				List<PictureCell> splitRow = splitRowColMapping.get(nextPageWidthNum);
				if (splitRow != null) {
					if (splitRow.size() > 0) {
						return true;
					}
				} else {
					break;
				}
			} while (true);
			
			return false;
		}
		
		private void writeSplitRowCells(Integer pageNum, PicturePageInfo pageInfo, PictureTableCellInfo cellInfo,
										   List<PictureCell> splitRowMapping) throws Exception {
			PictureCell firstCell = splitRowMapping.get(0);
			if (firstCell.nextSplitPageNum == pageNum.intValue()) {

				float onePageFitHeight = pageInfo.getActulOnePageFitHeight();
				
				List<PictureCell> list = new ArrayList<PictureCell>();
				for (PictureCell cell : splitRowMapping) {
					if (cell.drawSplitHeight + cell.drawSplitHeightOffset > onePageFitHeight) {
						float lastDrawHeight = cell.drawSplitHeight + cell.drawSplitHeightOffset - onePageFitHeight;
						float drawHeight = cell.drawSplitHeight - lastDrawHeight;
						cell.drawSplitHeight = drawHeight;

						writeRowCell(cell, pageInfo, cellInfo, false);
						
						list.add(cell);
						cell.rowSplitDrawYPos = pageInfo.topMargin;
						
						cell.drawSplitHeight = lastDrawHeight;
						
						cell.drawCompleteHeight += drawHeight;
					} else {
						writeRowCell(cell, pageInfo, cellInfo, true);
						
						cell.drawCompleteHeight = cell.maxHeight;
					}
				}
				
				if (splitRowMapping.size() != list.size()) {
					splitRowMapping.clear();
					for (int i = 0, j = list.size(); i < j; i++) {
						PictureCell cell = list.get(i);
						cell.nextSplitPageNum++;
						splitRowMapping.add(cell);
					}
				} else {  // $Date: 2017-11-30 - 没有绘制完毕nextSplitPageNum属性也要+1
					for (PictureCell cell : splitRowMapping) {
						cell.nextSplitPageNum++;
					}
				}
			}
		}
		
		/**
		 * 写入一行格子到画布
		 * 
		 * @param cell
		 * @param pageInfo
		 * @param cellInfo
		 * @param isDrawBottomBorder
		 * @throws Exception 
		 */
		public void writeRowCell(PictureCell cell, PicturePageInfo pageInfo, PictureTableCellInfo cellInfo, boolean isDrawBottomBorder) throws Exception {
			float drawSplitWidth = cell.drawWidth;
			float drawSplitHeight = cell.drawSplitHeight;
			if (cell.hasColSplit) {
				drawSplitWidth = cell.drawSplitWidth;
			}
			float x = cell.rowSplitDrawXPos;
			float y = cell.rowSplitDrawYPos;
			float w = drawSplitWidth;
			float h = drawSplitHeight;
			DrawRectangle rect = new DrawRectangle(x, y, w, h);
			
			if (cell.isHeader) {
				rect.setBorder(DrawRectangle.BOX);
				if (cell.hasColSplit) {
					if (cell.drawCompleteWidth > 0 && w == pageInfo.onePageFitWidth) {
						rect.drawBorderForceLeft = true;
						rect.drawBorderForceRight = true;
					} else if (cell.drawCompleteWidth > 0 && cell.drawCompleteWidth + w == cell.maxWidth) {
						rect.drawBorderForceLeft = true;
					} else {
						rect.drawBorderForceRight = true;
					}
				}
			} else {
				if (cell.hasColSplit) {
					float xPos = cellInfo.calculateXPos(cell);
					if (cell.drawCompleteWidth == cell.maxWidth) {
						//rect.setBorder(DrawRectangle.RIGHT);
						
						// $Date: 2016-12-8 - 新需求要画上左边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
					} else if (cell.rowSplitDrawXPos == xPos + pageInfo.leftMargin && cell.drawCompleteWidthXPos == 0){
						//rect.setBorder(DrawRectangle.LEFT);
						
						// $Date: 2016-12-8 - 新需求要画上右边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceRight = true;
					} else {
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
						rect.drawBorderForceRight = true;
					}
				} else {
					if (cell.hasRowColSplit) {
						//rect.setBorder(DrawRectangle.RIGHT);
						
						//$Date: 2016-12-8 - 新需求要画上左边框
						rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
						rect.drawBorderForceLeft = true;
					} else {
					    rect.setBorder(DrawRectangle.LEFT + DrawRectangle.RIGHT);
					}
				}
				
				if (isDrawBottomBorder) {
					if (rect.hasBorder()) {
						rect.setBorder(rect.getBorder() + DrawRectangle.BOTTOM);
					} else {
						rect.setBorder(DrawRectangle.BOTTOM);
					}
				}
			}

			writer.writeCellToPage(cell, rect, false, pageInfo, cellInfo);
		}
		
		/**
		 * 绘制单元格
		 * @param cell
		 * @param drawXPos
		 * @param drawYPos
		 * @param pageInfo
		 * @param cellInfo
		 * @param isSplitRow
		 * @param isSplitCol
		 * @throws Exception 
		 */
		private void writeCell(PictureCell cell, float drawXPos, float drawYPos, float fitYPos, PicturePageInfo pageInfo,
		                       PictureTableCellInfo cellInfo, boolean isSplitRow, boolean isSplitCol) throws Exception {

			// 注意: Picture绘制(0, 0)位于页面左上角, PDF绘制(0, 0)位于页面左下角
			//System.out.println("writeCell: (" + drawXPos + " , " + drawYPos + ") rowSplit[" + isSplitRow + "] colSplit[" + isSplitCol + "] text:" + cell);
			
			float drawXPosInt = drawXPos;
    		float leftMarginInt = pageInfo.leftMargin;
    		
    		DrawRectangle rect = null;
    		
	    	boolean isDrawWholeCell = false;
	    	if (!isSplitRow && !isSplitCol) {
	    		// 完整的画一个格子
	    		float w = cell.drawWidth;
	    		float h = cell.drawHeight;
	    		rect = new DrawRectangle(drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			// 如果是列切分的第一个格子, 则不需要画左边框
	    			//rect.setBorder(DrawRectangle.TOP + DrawRectangle.RIGHT + DrawRectangle.BOTTOM);
	    			
	    			// $Date: 2016-12-8 - 新需求要画上左边框
	    			rect.setBorder(DrawRectangle.BOX);
	    			rect.drawBorderForceLeft = true;
	    		} else {
	    			rect.setBorder(DrawRectangle.BOX);
	    			
	    			isDrawWholeCell = true;
	    		}

	    	} else if (isSplitRow && !isSplitCol) {
	    		// 只切分行
	    		float w = cell.drawWidth;
	    		float h = pageInfo.getActulPageFitHeight() - fitYPos;
	    		rect = new DrawRectangle(drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			//rect.setBorder(DrawRectangle.TOP + DrawRectangle.RIGHT);
	    			
	    			// $Date: 2016-12-8 - 新需求要画上左边框
	    			rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.RIGHT);
	    			rect.drawBorderForceLeft = true;
	    		} else {
	    			rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.RIGHT);
	    		}

	    	} else if (!isSplitRow && isSplitCol) {
	    		// 只切分列
	    		float w = pageInfo.pageFitWidth - drawXPos;
	    		float h = cell.drawHeight;
	    		rect = new DrawRectangle(drawXPos, drawYPos, w, h);
	    		
	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
	    			
//	    			if (cell.drawCompleteWidth + w == cell.maxWidth) {
//	    				rect.setBorder(DrawRectangle.TOP + DrawRectangle.RIGHT + DrawRectangle.BOTTOM);
//	    			} else {
//	    				rect.setBorder(DrawRectangle.TOP + DrawRectangle.BOTTOM);
//	    			}
	    			
	    			// $Date: 2016-12-8 - 新需求要画上左右边框
	    			rect.setBorder(DrawRectangle.BOX);
	    			rect.drawBorderForceLeft = true;
	    			rect.drawBorderForceRight = true;
	    		} else {
	    			//rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.BOTTOM);
	    			
	    			// $Date: 2016-12-8 - 新需求要画上右边框
	    			rect.setBorder(DrawRectangle.BOX);
	    			rect.drawBorderForceRight = true;
	    		}
	    		
	    	} else {
	    		// 行列都切分(通常是页面右下角超出页边部分)
	    		float w = pageInfo.pageFitWidth - drawXPos;
	    		float h = pageInfo.getActulPageFitHeight() - fitYPos;
	    		rect = new DrawRectangle(drawXPos, drawYPos, w, h);
	    		
//	    		if (cell.drawCompleteWidth > 0 && drawXPosInt == leftMarginInt && w < cell.maxWidth) {
//	    			// 如果是列切分的第一个格子, 则不需要画左边框
//	    			rect.setBorder(DrawRectangle.TOP);
//	    		} else {
//	    			rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT);
//	    		}
	    		
	    		// $Date: 2016-12-8 - 新需求要画上左右边框
	    		rect.setBorder(DrawRectangle.TOP + DrawRectangle.LEFT + DrawRectangle.RIGHT);
	    		rect.drawBorderForceLeft = true;
	    		rect.drawBorderForceRight = true;
	    	}
	    	
	    	writer.writeCellToPage(cell, rect, isDrawWholeCell, pageInfo, cellInfo);
		}
		
		/**
		 * 根据策略获得要绘制的格子信息
		 * 
		 * @param strategy
		 * @return
		 * @throws Exception
		 */
		private PictureTableCellInfo getPictureTableCellInfo(DrawStrategy strategy) throws Exception {
			
			Report report = this.report;

			ExcelWorkbook excel = this.workbook;

			String reportName = report.getSheetMapping().get(Const.MAIN);
			
			Sheet sheet = excel.getSheet(reportName);
			
			Set<ResultCell> visited = new HashSet<ResultCell>();
			int rowCount = report.getRowCount(reportName);
			int colCount = report.getColCount(reportName);
			
			PageCell pageCell = report.getPage(reportName);
			float pageCellFontWidth = pageCell.getFontWidth();
			
			float zoom = UNIT_ZOOM_FAC;
			float imageZoom = zoom;
			float fontZoom = 0f;
			//int totalWidth = 0; // use Picture unit: px
			int[] relativeWidths = new int[colCount];
			for (int col = 0; col < colCount; col++) {
				int width = getColWidth(sheet, col, pageCellFontWidth, zoom); // 格子大小不使用float方便计算
				relativeWidths[col] = width;
			}
			
			List<PictureCell> title  = new ArrayList<PictureCell>();
            List<PictureCell> header = new ArrayList<PictureCell>();
            List<PictureCell> picCells = new ArrayList<PictureCell>();
            List<PictureCell> footer = new ArrayList<PictureCell>();
            List<PictureCell> floating = new ArrayList<PictureCell>();

			// title-------------------------------------------------------------------------------------
			if (pageCell.getTitleCount() > 0) {
    			addPictureCell(title, report, excel, sheet, visited, 
    			           0, 
    			           pageCell.getTitleCount(), 
    			           zoom, fontZoom);
			}

			// header------------------------------------------------------------------------------------
			if (pageCell.getHeaderCount() > 0) {
				addPictureCell(header, report, excel, sheet, visited, 
    			           pageCell.getTitleCount(), 
    			           pageCell.getTitleCount() + pageCell.getHeaderCount(), 
    			           zoom, fontZoom);
			}

			// detail------------------------------------------------------------------------------------
			if (rowCount - pageCell.getFooterCount() > 0) {
				addPictureCell(picCells, report, excel, sheet, visited, 
    			           pageCell.getTitleCount() + pageCell.getHeaderCount(), 
    			           rowCount - pageCell.getFooterCount(), 
    			           zoom, fontZoom);
			}

			// footer------------------------------------------------------------------------------------
			if (pageCell.getFooterCount() > 0) {
				addPictureCell(footer, report, excel, sheet, visited, 
    			           rowCount - pageCell.getFooterCount(), 
    			           rowCount, 
    			           zoom, fontZoom);
			}

			// floatings---------------------------------------------------------------------------------
			Set<ResultCell> cells = report.getImages(reportName).keySet();
			// Date: 2017-06-16 - 高度不缩放, 图片大小不改变
			imageZoom = UNIT_ZOOM_FAC;
			for(ResultCell rc: cells){
				if(rc instanceof FloatingResultCell){
					int top = 0;
					for(int r = 0; r < rc.getRbegin(); r++){
						top += (getRowHeight(sheet, r, UNIT_ZOOM_FAC/*zoom*/));
					}
					int left = 0;
					for(int c = 0; c < rc.getCbegin(); c++){
						left += (getColWidth(sheet, c, pageCellFontWidth, zoom));
					}
					
					PictureCell xcell = new PictureCell();
					xcell.rowIndex = rc.getRbegin();
					xcell.colIndex = rc.getCbegin();
					xcell.rowspan = rc.getRowInc();
					xcell.colspan = rc.getColInc();
					//xcell.border = DrawRectangle.NO_BORDER;

					PictureImage img = getImage(rc);
					
					int width = (int) getCellPixelWidth(sheet, rc, pageCellFontWidth);
					int height = (int) getCellPixelHeight(sheet, rc);
					img.update(width, height);
					
					xcell.image = img;
					xcell.maxWidth = (int)Math.floor(img.getScaledWidth() * imageZoom);
					xcell.maxHeight = (int)Math.floor(img.getScaledHeight() * imageZoom);

					xcell.xPos = left;
					xcell.yPos = top;
					floating.add(xcell);
				}
			}
			
			PictureTableCellInfo tableCellInfo = new PictureTableCellInfo(title, header, picCells, footer, floating, relativeWidths, strategy);
			
			tableCellInfo.cellZoom = zoom;
			tableCellInfo.imageZoom = imageZoom;
			tableCellInfo.fontZoom = fontZoom;
			
			tableCellInfo.colCount = colCount;
			tableCellInfo.rowCount = rowCount;
			
			return tableCellInfo;
		}
		
		private void addPictureCell(List<PictureCell> picCells, Report report, ExcelWorkbook excel, Sheet sheet, Set<ResultCell> visited, 
		                         int rowStartIndex, int rowEndIndex, float zoom, float fontZoom) throws Exception {
			Map<Integer, List<PictureCell>> rowCellMapping = null;
			Set<Integer> rowCellRowIndexSet = null;
			String reportName = report.getSheetMapping().get(Const.MAIN);
			PageCell pageCell = report.getPage(reportName);
			if (pageCell.isDynamicSize()) {
				rowCellMapping = new HashMap<Integer, List<PictureCell>>();
				rowCellRowIndexSet = new HashSet<Integer>();
			}
			
			int colCount = report.getColCount(reportName);
			
			for (int rowIndex = rowStartIndex; rowIndex < rowEndIndex; rowIndex++) {
				
				for (int colIndex = 0; colIndex < colCount; colIndex++) {
					PictureCell xcell = null;
					
					ResultCell cell = report.getCell(reportName, rowIndex, colIndex);
					int rowspan = 1;
					int colspan = 1;
					if (cell == null) {
						xcell = new PictureCell();
						picCells.add(xcell);
						xcell.rowspan = rowspan;
						xcell.colspan = colspan;
						//xcell.border = DrawRectangle.NO_BORDER;
						xcell.rowIndex = rowIndex;
						xcell.colIndex = colIndex;
					} else if (!visited.contains(cell)) {
						visited.add(cell);
						
						xcell = new PictureCell();
						picCells.add(xcell);
						rowspan = cell.getRowInc();
						colspan = cell.getColInc();
						xcell.rowspan = rowspan;
						xcell.colspan = colspan;
						xcell.rowIndex = rowIndex;
						xcell.colIndex = colIndex;
						
						Cell excelCell = sheet.getRow(rowIndex).getCell(colIndex);
						
						generateCellPreviewContent(cell, excelCell, zoom, fontZoom, excel, sheet, xcell);
					}
					
					if (xcell != null) {
						// Date: 2017-06-16 - 高度不缩放
						int fixedHeight = getRowHeight(sheet, rowIndex, UNIT_ZOOM_FAC/*zoom*/);
						int rowspanNum = xcell.rowspan;
						if (rowspanNum > 1) {
							for (int i = 1, j = rowIndex + 1; i < rowspanNum; i++, j++) {
								fixedHeight += getRowHeight(sheet, j, UNIT_ZOOM_FAC/*zoom*/);
							}
						}
						xcell.maxHeight = fixedHeight;
						
						// autoSize
						if (rowCellMapping != null) {
							Integer key = Integer.valueOf(rowIndex);
    						List<PictureCell> rowCellList = rowCellMapping.get(key);
    						if (rowCellList == null) {
    							rowCellList = new ArrayList<PictureCell>();
    							rowCellMapping.put(Integer.valueOf(rowIndex), rowCellList);
    						}
    						rowCellList.add(xcell);
    						if (cell != null && !rowCellRowIndexSet.contains(key) && cell.isRowAutoSize(rowIndex, pageCell)) {
    							rowCellRowIndexSet.add(key);
    						}
						}
					}
				}
			}
			
			// autoSize
			if (rowCellMapping != null) {
				for (Iterator<Integer> iter = rowCellRowIndexSet.iterator(); iter.hasNext(); ) {
					List<PictureCell> rowCellList = rowCellMapping.get(iter.next());
					for (PictureCell xcell : rowCellList) {
						xcell.textControl = TextControlEnum.AUTOSIZE;
					}
				}
			}
		}
		
		/**
		 * @see com.yss.sofa.report.engine.Engine#generateCellPreviewContent
		 * @param cell
		 * @param excelCell
		 * @param evaluator
		 * @param zoom
		 * @param fontZoom
		 * @param excel
		 * @param sheet
		 * @param xcell
		 */
		private void generateCellPreviewContent(ResultCell cell, Cell excelCell, float zoom, float fontZoom, 
												ExcelWorkbook excel, Sheet sheet, PictureCell xcell) throws Exception {
			// CSS - background,font,align,border
			CellStyle cellStyle = excelCell.getCellStyle();
			
			if(cell instanceof ChartResultCell){
				PictureImage img = getImage(cell);

				float pageCellFontWidth = report.getPage(report.getSheetMapping().get(Const.MAIN)).getFontWidth();
				int width = (int) getCellPixelWidth(sheet, cell, pageCellFontWidth);
				int height = (int) getCellPixelHeight(sheet, cell);
				img.update(width, height);
				
				xcell.image = img;
				xcell.textControl = TextControlEnum.CLIP;
				xcell.textDrawControl = TextControlEnum.DIRECT;
			} else {
				String text = excel.getExcelCellFormattedValue(cell, excelCell);
				if(cellStyle instanceof XSSFCellStyle){
					XSSFFont font = ((XSSFCellStyle)cellStyle).getFont();
					setTextByExcel(xcell, text.replaceAll("\r", ""), font, fontZoom);
					
					xcell.horizontalAlignment = getHAlignByExcel(cellStyle);
					xcell.verticalAlignment = getVAlignByExcel(cellStyle);
					
					setTextControlByExcel(xcell,cellStyle);
				}else if(cellStyle instanceof HSSFCellStyle && sheet instanceof HSSFSheet){
					HSSFFont font = ((HSSFCellStyle)cellStyle).getFont(excelCell.getRow().getSheet().getWorkbook());
					setTextByExcel(xcell, text.replaceAll("\r", ""), font,(HSSFSheet)sheet, fontZoom);
					
					xcell.horizontalAlignment = getHAlignByExcel(cellStyle);
					xcell.verticalAlignment = getVAlignByExcel(cellStyle);
					
					setTextControlByExcel(xcell,cellStyle);
				}
			}
			
			if(cellStyle instanceof XSSFCellStyle && sheet instanceof XSSFSheet){
				xcell.backgroundColor = getBackgroundColorByExcel((XSSFCellStyle)cellStyle);
				setBorderByExcel(xcell, (XSSFSheet)sheet, (XSSFCellStyle)cellStyle);
			}else if(cellStyle instanceof HSSFCellStyle && sheet instanceof HSSFSheet){
				xcell.backgroundColor = getBackgroundColorByExcel03((HSSFCellStyle)cellStyle);
				setBorderByExcel03(xcell, (HSSFSheet)sheet, (HSSFCellStyle)cellStyle);
			}
			
		}
		
		/**
		 * 获得格子中的图形
		 * @param report
		 * @param cell
		 * @return
		 */
		private PictureImage getImage(ResultCell cell) throws Exception {
			byte[] imgb = report.getImages(Const.MAIN).get(cell);
			PictureImage img = new PictureImage(imgb);
			return img;
		}
		
		/**
		 * 设置Excel对应的Picture边框样式.<br/>
		 * POI: getBorderXXX() Deprecated. 3.15 beta 2. Use getBorderXXXEnum() instead.
		 * @param xcell
		 * @param sheet
		 * @param style
		 */
		private void setBorderByExcel(PictureCell xcell, XSSFSheet sheet, XSSFCellStyle style) {
			BorderRoundRectangle rect = new BorderRoundRectangle();
			
			XSSFCellStyle xssfCellStyle = (XSSFCellStyle) style;
			
			rect.topBorder = toPictureBorderStyle(xssfCellStyle.getBorderTopEnum(), xssfCellStyle.getBorderColor(BorderSide.TOP));
			rect.leftBorder = toPictureBorderStyle(xssfCellStyle.getBorderLeftEnum(), xssfCellStyle.getBorderColor(BorderSide.LEFT));
			
			// 合并单元格边框判断如下
			BorderStyle rbs = xssfCellStyle.getBorderRightEnum();
			if (xcell.colspan > 1 && rbs == BorderStyle.NONE) {
				XSSFCell lastMergeCell = sheet.getRow(xcell.rowIndex).getCell(xcell.colIndex + xcell.colspan - 1);
				XSSFCellStyle lastMergeCellStyle = (XSSFCellStyle) lastMergeCell.getCellStyle();
				rect.rightBorder = toPictureBorderStyle(lastMergeCellStyle.getBorderRightEnum(), lastMergeCellStyle.getBorderColor(BorderSide.RIGHT));
			} else {
				rect.rightBorder = toPictureBorderStyle(rbs, xssfCellStyle.getBorderColor(BorderSide.RIGHT));
			}
			
			BorderStyle bbs = xssfCellStyle.getBorderBottomEnum();
			if (xcell.rowspan > 1 && bbs == BorderStyle.NONE) {
				XSSFCell lastMergeCell = sheet.getRow(xcell.rowIndex + xcell.rowspan - 1).getCell(xcell.colIndex);
				XSSFCellStyle lastMergeCellStyle = (XSSFCellStyle) lastMergeCell.getCellStyle();
				rect.bottomBorder = toPictureBorderStyle(lastMergeCellStyle.getBorderBottomEnum(), lastMergeCellStyle.getBorderColor(BorderSide.BOTTOM));
			} else {
				rect.bottomBorder = toPictureBorderStyle(bbs, xssfCellStyle.getBorderColor(BorderSide.BOTTOM));
			}
			
			xcell.topBorderWidth = rect.topBorder != null ? (int)rect.topBorder.getBorderWidth() : 0;
			xcell.leftBorderWidth = rect.leftBorder != null ? (int)rect.leftBorder.getBorderWidth() : 0;
			xcell.bottomBorderWidth = rect.bottomBorder != null ? (int)rect.bottomBorder.getBorderWidth() : 0;
			xcell.rightBorderWidth = rect.rightBorder != null ? (int)rect.rightBorder.getBorderWidth() : 0;
			
			xcell.borderRoundRectangle = rect;
		}
		
		/**
		 * 设置Excel对应的Picture边框样式.<br/>
		 * POI: getBorderXXX() Deprecated. 3.15 beta 2. Use getBorderXXXEnum() instead.
		 * @param xcell
		 * @param sheet
		 * @param style
		 */
		private void setBorderByExcel03(PictureCell xcell, HSSFSheet sheet, HSSFCellStyle style) {
			BorderRoundRectangle rect = new BorderRoundRectangle();
			
			HSSFCellStyle hssfCellStyle = (HSSFCellStyle) style;
			HSSFPalette palette = sheet.getWorkbook().getCustomPalette();
			rect.topBorder = toPictureBorderStyle03(hssfCellStyle.getBorderTopEnum(), palette.getColor(hssfCellStyle.getTopBorderColor()));
			rect.leftBorder = toPictureBorderStyle03(hssfCellStyle.getBorderLeftEnum(), palette.getColor(hssfCellStyle.getLeftBorderColor()));
			
			// 合并单元格边框判断如下
			BorderStyle rbs = hssfCellStyle.getBorderRightEnum();
			if (xcell.colspan > 1 && rbs == BorderStyle.NONE) {
				HSSFCell lastMergeCell = sheet.getRow(xcell.rowIndex).getCell(xcell.colIndex + xcell.colspan - 1);
				HSSFCellStyle lastMergeCellStyle = lastMergeCell.getCellStyle();
				rect.rightBorder = toPictureBorderStyle03(lastMergeCellStyle.getBorderRightEnum(), palette.getColor(lastMergeCellStyle.getRightBorderColor()));
			} else {
				rect.rightBorder = toPictureBorderStyle03(rbs, palette.getColor(hssfCellStyle.getRightBorderColor()));
			}
			
			BorderStyle bbs = hssfCellStyle.getBorderBottomEnum();
			if (xcell.rowspan > 1 && bbs == BorderStyle.NONE) {
				HSSFCell lastMergeCell = sheet.getRow(xcell.rowIndex + xcell.rowspan - 1).getCell(xcell.colIndex);
				HSSFCellStyle lastMergeCellStyle = lastMergeCell.getCellStyle();
				rect.bottomBorder = toPictureBorderStyle03(lastMergeCellStyle.getBorderBottomEnum(), palette.getColor(lastMergeCellStyle.getBottomBorderColor()));
			} else {
				rect.bottomBorder = toPictureBorderStyle03(bbs, palette.getColor(hssfCellStyle.getBottomBorderColor()));
			}
			
			xcell.topBorderWidth = rect.topBorder != null ? (int)rect.topBorder.getBorderWidth() : 0;
			xcell.leftBorderWidth = rect.leftBorder != null ? (int)rect.leftBorder.getBorderWidth() : 0;
			xcell.bottomBorderWidth = rect.bottomBorder != null ? (int)rect.bottomBorder.getBorderWidth() : 0;
			xcell.rightBorderWidth = rect.rightBorder != null ? (int)rect.rightBorder.getBorderWidth() : 0;
			
			xcell.borderRoundRectangle = rect;
		}
		
		
		/**
		 * Picture边框样式的单位是磅(默认0.5磅, 最小0.25磅, 最大3~6磅). 这与PDF一致. 但是Excel没有边框粗细设置, 使用默认的0.5磅对应所有单线边框.<br/>
		 * Excel 2013边框有13种样式, Picture 2013有24种. 粗细使用THIN:0.5, MEDIUM:1.5, THICK:2.25
		 * @param border
		 * @param color
		 * @return
		 */
		private PictureBorderStyle toPictureBorderStyle(BorderStyle border, XSSFColor color){
			PictureBorderStyle style = null;
			float width = 0f;
			switch (border) {
			case NONE:
				break;
			case THIN:
				width=1; // px
				style = new LineBorderStyle(width, toPictureColor(color));
				break;
			case MEDIUM:
				width=2;
				style = new LineBorderStyle(width, toPictureColor(color));
				break;
			case DASHED:
				width=1;
				style = new DashedBorderStyle(width, toPictureColor(color));
				break;
			case DOTTED:
				width=1;
				style = new DottedBorderStyle(width, toPictureColor(color));
				break;
			case THICK:
				width=3;
				style = new LineBorderStyle(width, toPictureColor(color));
				break;
			case DOUBLE:
				//width=3;
				width = 0.5f;
				style = new DoubleBorderStyle(width, toPictureColor(color));
				break;
			case HAIR:
				width=1;
				style = new DottedBorderStyle(width, toPictureColor(color));
				break;
			case MEDIUM_DASHED:
				width=2;
				style = new DashedBorderStyle(width, toPictureColor(color));
				break;
			case DASH_DOT:
				width=1;
				style = new DashDotBorderStyle(width, toPictureColor(color));
				break;
			case MEDIUM_DASH_DOT:
				width=2;
				style = new DashDotBorderStyle(width, toPictureColor(color));
				break;
			case DASH_DOT_DOT:
				width=1;
				style = new DashDotBorderStyle(width, toPictureColor(color));
				break;
			case MEDIUM_DASH_DOT_DOT:
				width=2;
				style = new DashDotBorderStyle(width, toPictureColor(color));
				break;
			case SLANTED_DASH_DOT:
				//width=2;
				width = 3f;
				style = new DashDotBorderStyle(width, toPictureColor(color));
				break;
			default:
				break;
			}

			return style;
		}
		

		/**
		 * Picture边框样式的单位是磅(默认0.5磅, 最小0.25磅, 最大3~6磅). 这与PDF一致. 但是Excel没有边框粗细设置, 使用默认的0.5磅对应所有单线边框.<br/>
		 * Excel 2013边框有13种样式, Picture 2013有24种. 粗细使用THIN:0.5, MEDIUM:1.5, THICK:2.25
		 * @param border
		 * @param color
		 * @return
		 */
		private PictureBorderStyle toPictureBorderStyle03(BorderStyle border, HSSFColor color){
			PictureBorderStyle style = null;
			float width = 0f;
			switch (border) {
			case NONE:
				break;
			case THIN:
				width=1; // px
				style = new LineBorderStyle(width, toPictureColor03(color));
				break;
			case MEDIUM:
				width=2;
				style = new LineBorderStyle(width, toPictureColor03(color));
				break;
			case DASHED:
				width=1;
				style = new DashedBorderStyle(width, toPictureColor03(color));
				break;
			case DOTTED:
				width=1;
				style = new DottedBorderStyle(width, toPictureColor03(color));
				break;
			case THICK:
				width=3;
				style = new LineBorderStyle(width, toPictureColor03(color));
				break;
			case DOUBLE:
				//width=3;
				width = 0.5f;
				style = new DoubleBorderStyle(width, toPictureColor03(color));
				break;
			case HAIR:
				width=1;
				style = new DottedBorderStyle(width, toPictureColor03(color));
				break;
			case MEDIUM_DASHED:
				width=2;
				style = new DashedBorderStyle(width, toPictureColor03(color));
				break;
			case DASH_DOT:
				width=1;
				style = new DashDotBorderStyle(width, toPictureColor03(color));
				break;
			case MEDIUM_DASH_DOT:
				width=2;
				style = new DashDotBorderStyle(width, toPictureColor03(color));
				break;
			case DASH_DOT_DOT:
				width=1;
				style = new DashDotBorderStyle(width, toPictureColor03(color));
				break;
			case MEDIUM_DASH_DOT_DOT:
				width=2;
				style = new DashDotBorderStyle(width, toPictureColor03(color));
				break;
			case SLANTED_DASH_DOT:
				//width=2;
				width = 3f;
				style = new DashDotBorderStyle(width, toPictureColor03(color));
				break;
			default:
				break;
			}

			return style;
		}
		
		
		/**
		 * 获得Excel单元格底纹颜色
		 * @param style
		 * @return
		 */
		private java.awt.Color getBackgroundColorByExcel(XSSFCellStyle style) {
			// 单元格格式-图案-单元格底纹
			XSSFColor color = style.getFillForegroundColorColor();
			if (color == null) {
				return null; // 优化提示:白色背景不画
			}
			return toPictureColor(color);
		}
		
		/**
		 * 获得Excel单元格底纹颜色
		 * @param style
		 * @return
		 */
		private java.awt.Color getBackgroundColorByExcel03(HSSFCellStyle style) {
			// 单元格格式-图案-单元格底纹
			HSSFColor color = style.getFillForegroundColorColor();
			if (color == null) {
				return null; // 优化提示:白色背景不画
			}
			return toPictureColor03(color);
		}
		
		/**
		 * 过长或过大的文本内容是否进行自动换行,缩放填充,自动截取.<br/>
		 * 1. 自动换行(设置单元格格式-对齐-文本控制). 自动换行并固定高度不处理(Excel设置为一个实心点, 可以设置自动换行后改变格子高度)<br/>
		 * 2. 缩小填充(设置单元格格式-对齐-文本控制)。只有文本宽度大于格子宽度才会缩放到适合格子大小<br/>
		 * 3. 截断
		 * @param xcell
		 * @param style
		 * @see com.yss.sofa.report.engine.util.PrintUtil toPrintTextStretch
		 */
		private void setTextControlByExcel(PictureCell xcell, CellStyle style) {
			if (style.getWrapText()) {
				xcell.textControl = TextControlEnum.STRETCH;
			} else if (style.getShrinkToFit()) {
				xcell.textControl = TextControlEnum.SHRINK;
			} else {
				xcell.textControl = TextControlEnum.CLIP;
			}
		}
		
		/**
		 * 获得Excel对应的Picture水平对齐方式的值.<br/>
		 * Excel 2007: 常规(数值靠右,布尔(true/false)居中,其他靠左),靠左,居中,靠右,填充,两端对齐,跨列居中,分散对齐
		 * 
		 * @see org.apache.poi.ss.usermodel.CellStyle
		 * @param style
		 * @return
		 */
		private HVAlignment getHAlignByExcel(CellStyle style) {
			HVAlignment result = null;
			switch (style.getAlignmentEnum()) {
				case CENTER : {
					result = HVAlignment.CENTER;
					break;
				}
				case RIGHT : {
					result = HVAlignment.RIGHT;
					break;
				}
				default : {
					result = HVAlignment.LEFT;
					break;
				}
			}
			return result;
		}

		/**
		 * 获得Excel对应的Picture垂直对齐方式的值.<br/>
		 * Excel 2007: 靠上,居中,靠下,两端对齐,分散对齐
		 * 
		 * @see org.apache.poi.ss.usermodel.CellStyle
		 * @param style
		 * @return
		 */
		private HVAlignment getVAlignByExcel(CellStyle style) {
			HVAlignment result = null;
			switch (style.getVerticalAlignmentEnum()) {
				case TOP : {
					result = HVAlignment.TOP;
					break;
				}
				case CENTER : {
					result = HVAlignment.MIDDLE;
					break;
				}
				case BOTTOM : {
					result = HVAlignment.BOTTOM;
					break;
				}
				case JUSTIFY : {
					result = HVAlignment.TOP;
					break;
				}
				default : {
					result = HVAlignment.TOP;
					break;
				}
			}
			return result;
		}
		
		/**
		 * 设置Excel对应的Picture文本内容
		 * @param xcell
		 * @param text
		 * @param font
		 * @param fontZoom
		 */
		private void setTextByExcel(PictureCell xcell, String text, XSSFFont font, float fontZoom) {
			
			PictureFont firstFont = getFontByExcel(font);
			
			// 页面放大字体大小不变, 缩小要变化
			// $Date: 2017-06-16 - 缩小也不变
			//if (fontZoom > 0 && fontZoom < 1) {
			//	firstFont.fontSize = (int)(firstFont.fontSize * fontZoom);
			//}
			
			xcell.cellText = text;  // phrase[chunk, chunk, ...]
			xcell.cellFont = firstFont;
		}
		
		/**
		 * 设置Excel对应的Picture文本内容
		 * @param xcell
		 * @param text
		 * @param font
		 * @param fontZoom
		 */
		private void setTextByExcel(PictureCell xcell, String text, HSSFFont font,HSSFSheet sheet, float fontZoom) {
			
			PictureFont firstFont = getFontByExcel(font,sheet);
			
			// 页面放大字体大小不变, 缩小要变化
			// $Date: 2017-06-16 - 缩小也不变
			//if (fontZoom > 0 && fontZoom < 1) {
			//	firstFont.fontSize = (int)(firstFont.fontSize * fontZoom);
			//}
			
			xcell.cellText = text;  // phrase[chunk, chunk, ...]
			xcell.cellFont = firstFont;
		}
		
		
		/**
		 * 获得Excel对应的Picture字体及其样式
		 * @param font
		 * @return
		 */
		private static PictureFont getFontByExcel(XSSFFont font) {
			
			int fontStyle = PictureFont.NORMAL;

			// 加粗
			if (font.getBold()) {
				fontStyle = fontStyle | PictureFont.BOLD;
			}

			// 斜体
			if (font.getItalic()) {
				fontStyle = fontStyle | PictureFont.ITALIC;
			}

			// 下划线
			FontUnderline underline = FontUnderline.valueOf(font.getUnderline());
			if (underline == FontUnderline.SINGLE) {
				fontStyle = fontStyle | PictureFont.UNDERLINE;
			}
			
			// 删除线
			if (font.getStrikeout()) {
				fontStyle = fontStyle | PictureFont.STRIKETHRU;
			}
			
			// 颜色
			XSSFColor color = font.getXSSFColor();

			// 默认字体由绘制时JRE自己映射
			Color fontColor = Color.BLACK;
			if (color != null) {
				fontColor = toPictureColor(color); // 字体默认为黑色
			}
			// 获得适合的字体
			PictureFont result = new PictureFont(font.getFontName(), font.getFontHeightInPoints(), fontStyle, fontColor);
			Font fitFont = result.getFitFont();
			String fontName = FontFactory.getFont(fitFont.getFamily(), fitFont.getName());
			result.fontName = fontName;

			return result;
		}
		
		/**
		 * 获得Excel对应的Picture字体及其样式
		 * @param font
		 * @return
		 */
		private static PictureFont getFontByExcel(HSSFFont font,HSSFSheet sheet) {
			
			int fontStyle = PictureFont.NORMAL;

			// 加粗
			if (font.getBold()) {
				fontStyle = fontStyle | PictureFont.BOLD;
			}

			// 斜体
			if (font.getItalic()) {
				fontStyle = fontStyle | PictureFont.ITALIC;
			}

			// 下划线
			FontUnderline underline = FontUnderline.valueOf(font.getUnderline());
			if (underline == FontUnderline.SINGLE) {
				fontStyle = fontStyle | PictureFont.UNDERLINE;
			}
			
			// 删除线
			if (font.getStrikeout()) {
				fontStyle = fontStyle | PictureFont.STRIKETHRU;
			}
			
			// 颜色
			HSSFColor color = font.getHSSFColor(sheet.getWorkbook());

			// 默认字体由绘制时JRE自己映射
			Color fontColor = Color.BLACK;
			if (color != null) {
				fontColor = toPictureColor03(color); // 字体默认为黑色
			}
			// 获得适合的字体
			PictureFont result = new PictureFont(font.getFontName(), font.getFontHeightInPoints(), fontStyle, fontColor);
			Font fitFont = result.getFitFont();
			String fontName = FontFactory.getFont(fitFont.getFamily(), fitFont.getName());
			result.fontName = fontName;

			return result;
		}
		
		/**
		 * 获得Excel对应的Picture字体及其样式
		 * @param font
		 * @return
		 */
		private static PictureFont getFontByExcel03(HSSFFont font,Sheet sheet) {
			
			int fontStyle = PictureFont.NORMAL;

			// 加粗
			if (font.getBold()) {
				fontStyle = fontStyle | PictureFont.BOLD;
			}

			// 斜体
			if (font.getItalic()) {
				fontStyle = fontStyle | PictureFont.ITALIC;
			}

			// 下划线
			FontUnderline underline = FontUnderline.valueOf(font.getUnderline());
			if (underline == FontUnderline.SINGLE) {
				fontStyle = fontStyle | PictureFont.UNDERLINE;
			}
			
			// 删除线
			if (font.getStrikeout()) {
				fontStyle = fontStyle | PictureFont.STRIKETHRU;
			}
			
			// 颜色
			
			HSSFPalette palette = ((HSSFWorkbook) sheet.getWorkbook()).getCustomPalette();
			HSSFColor color =palette.getColor(font.getColor());
			
			// 默认字体由绘制时JRE自己映射
			Color fontColor = Color.BLACK;
			if (color != null) {
				fontColor = toPictureColor03(color); // 字体默认为黑色
			}
			// 获得适合的字体
			PictureFont result = new PictureFont(font.getFontName(), font.getFontHeightInPoints(), fontStyle, fontColor);
			Font fitFont = result.getFitFont();
			String fontName = FontFactory.getFont(fitFont.getFamily(), fitFont.getName());
			result.fontName = fontName;

			return result;
		}
		
		/**
		 * 获得颜色的Hex字符串形式. 255=>FF, 0=>00
		 * @param color
		 * @return
		 */
		private static java.awt.Color toPictureColor(XSSFColor color) {
			if (color == null) {
				return Color.BLACK;
			}
			
			int value = getRGB(color);
			if (value == 0) {
				return Color.BLACK;
			}
			
			return new java.awt.Color(value);
		}
		
		/**
		 * 获得颜色的Hex字符串形式. 255=>FF, 0=>00
		 * @param color
		 * @return
		 */
		private static java.awt.Color toPictureColor03(HSSFColor color) {
			if (color == null) {
				return Color.BLACK;
			}
			
			int value = getRGB(color);
			if (value == 0) {
				return Color.BLACK;
			}
			
			return new java.awt.Color(value);
		}
		
		
		/**
		 * 计算颜色的RGB值
		 * @param color
		 * @return
		 */
		public static int getRGB(XSSFColor color) {

			int result = 0x00000000; //0x00FFFFFF; ARGB

			if (color == null) {
				return result;
			}

			int red = 0;
			int green = 0;
			int blue = 0;

			XSSFColor xssfColor = (XSSFColor) color;
			byte[] rgb = xssfColor.getRGBWithTint();
			if (rgb == null) {
				return result;
			}
			red = (rgb[0] < 0) ? (rgb[0] + 256) : rgb[0];
			green = (rgb[1] < 0) ? (rgb[1] + 256) : rgb[1];
			blue = (rgb[2] < 0) ? (rgb[2] + 256) : rgb[2];

			if (red != 0 || green != 0 || blue != 0) {
				//result = new java.awt.Color(red, green, blue).getRGB();
				result = ((255 & 0xFF) << 24) |
		                ((red & 0xFF) << 16) |
		                ((green & 0xFF) << 8)  |
		                ((blue & 0xFF) << 0);
			}

			return result;
		}
		
		/**
		 * 计算颜色的RGB值
		 * @param color
		 * @return
		 */
		public static int getRGB(HSSFColor color) {

			int result = 0x00000000; //0x00FFFFFF; ARGB

			if (color == null) {
				return result;
			}
			int red = 0;
			int green = 0;
			int blue = 0;
			
			HSSFColor hssfColor = (HSSFColor) color;
			short[] rgb = hssfColor.getTriplet();
			if (rgb == null) {
				return result;
			}
			red = (rgb[0] < 0) ? (rgb[0] + 256) : rgb[0];
			green = (rgb[1] < 0) ? (rgb[1] + 256) : rgb[1];
			blue = (rgb[2] < 0) ? (rgb[2] + 256) : rgb[2];

			if (red != 0 || green != 0 || blue != 0) {
				//result = new java.awt.Color(red, green, blue).getRGB();
				result = ((255 & 0xFF) << 24) |
		                ((red & 0xFF) << 16) |
		                ((green & 0xFF) << 8)  |
		                ((blue & 0xFF) << 0);
			}

			return result;
		}
		
		/**
		 * @see com.yss.sofa.report.engine.Engine#getColPixelWidth
		 * @param sheet
		 * @param col
		 * @param fontWidth
		 * @param zoom
		 * @return
		 */
		private int getColWidth(Sheet sheet, int col, float fontWidth, float zoom) {

			if (sheet.isColumnHidden(col)) {
				return 1;
			}
			// sheet.getColumnWidth(), get the width (in units of 1/256th of a character width)
			float widthIn256 = sheet.getColumnWidth(col) * zoom;
			return (int)((widthIn256 / 256 * fontWidth) + 1);
		}

		/**
		 * @see com.yss.sofa.report.engine.Engine#getRowPixelHeight
		 * @param sheet
		 * @param row
		 * @param zoom
		 * @return
		 */
		private int getRowHeight(Sheet sheet, int row, float zoom) {

			if (sheet.getRow(row).getZeroHeight()) {
				return 1;
			}
			float h = sheet.getRow(row).getHeight() * zoom / 15f;
			return (int)(h + 1);// (h*96f/72f)+1;;
		}
		
		private int getCellPixelWidth(Sheet sheet, ResultCell cell, float fontWidth) {
			float width = 0;
			for(int begin = cell.getCbegin(), end = cell.getCend() + 1; begin < end; ++begin){
				if (sheet.isColumnHidden(begin)) {
					width += 1;
				} else {
					width += sheet.getColumnWidth(begin);
				}
			}
			return (int)((width / 256 * fontWidth) + 1);
		}
		
		private int getCellPixelHeight(Sheet sheet, ResultCell cell) {
			float height = 0;
			for(int begin = cell.getRbegin(), end = cell.getRend() + 1; begin < end; ++begin){
				if (sheet.isColumnHidden(begin)) {
					height += 1;
				} else {
					height += (sheet.getRow(begin).getHeight() + 1);
				}
			}
			return (int)((height / 15f + 1));
		}
		
		// 策略使用方法
		/**
		 * 设置单元格文本内容绘制方式. 要求格子有高宽和文本控制设置
		 * @param cell
		 */
		private void setTextDrawControl(PictureCell cell) {
			if (cell.textControl == null) {
				cell.textControl = TextControlEnum.CLIP;
				cell.textDrawControl = TextControlEnum.DIRECT;
				return;
			}
			
			PictureTextInfo textInfo = PictureTextInfo.getPictureTextInfo(cell, writer);
			if (textInfo == null) {
				return;
			}
			
			float drawWidth = cell.maxWidth;
			float drawHeight = cell.maxHeight;
			float cellContentWidth = drawWidth - cell.leftBorderWidth - cell.rightBorderWidth;
			float cellContentHeight = drawHeight - cell.topBorderWidth - cell.bottomBorderWidth;
			
			float textWidth = textInfo.getPictureTextWidth();
			float textHeight = textInfo.getPictureTextHeight();
			
			switch (cell.textControl) {
				case CLIP : {
					if (textWidth > cellContentWidth || textHeight > cellContentHeight) {
						cell.textDrawControl = TextControlEnum.CLIP;
					} else {
						cell.textDrawControl = TextControlEnum.DIRECT;
					}
					break;
				}
				case STRETCH : {
					if (textWidth <= cellContentWidth && textHeight <= cellContentHeight) {
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else if (textWidth <= cellContentWidth) {
						if (textHeight <= cellContentHeight) {
							cell.textDrawControl = TextControlEnum.DIRECT;
						} else {
							cell.textDrawControl = TextControlEnum.CLIP;
						}
					} else {
						setAutoSize(cell, textInfo, cellContentWidth, cellContentHeight, drawHeight);
					}
						
					break;
				}
				case SHRINK : {
					if (textWidth > cellContentWidth) {
						// 改变字体大小, 使其内容高宽都能容纳在格子中
						float wf = 1f * cellContentWidth / textWidth;
						if (textHeight * wf > cellContentHeight) {
							float hf = cellContentHeight / (textHeight * wf);
							wf = wf * hf;
						}
						wf -= 0.001f;
						if (wf < 0) {
							wf = 0;
						}
						
						cell.cellFont.fontSize = (int)(cell.cellFont.fontSize * wf);
						cell.textInfo = null;
						PictureTextInfo newTextInfo = PictureTextInfo.getPictureTextInfo(cell, writer);
						
						cell.textInfo = newTextInfo;
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else if (textHeight > cellContentHeight) {
						cell.textDrawControl = TextControlEnum.CLIP;
					} else {
						cell.textDrawControl = TextControlEnum.DIRECT;
					}
					break;
				}
				case AUTOSIZE : {
					if (textWidth <= cellContentWidth && textHeight <= cellContentHeight) {
						cell.textDrawControl = TextControlEnum.DIRECT;
					} else {
						setAutoSize(cell, textInfo, cellContentWidth, cellContentHeight, drawHeight);
					}
					break;
				}
				default : {
					cell.textDrawControl = TextControlEnum.DIRECT;
					break;
				}
			}
		}
		
		private void setAutoSize(PictureCell cell, PictureTextInfo textInfo, float cellContentWidth, float cellContentHeight, float drawHeight) {
			// 格子宽度不变, 用现有文本内容计算自动换行后需要的高度, 然后比较格子现高度是否小于计算的文本内容高度, 如果小于就改变格子的高度.
			float limitWidth = cellContentWidth - 2; // 是格子内容不要太挤
			float newHeight = PictureTextInfo.calculateTextHeight(cell, limitWidth, true, writer);
			
			if (newHeight >= cellContentHeight) {
				Chunk chunk = textInfo.chunkList.get(textInfo.chunkList.size() - 1);
				float desc = -2*chunk.desc; //-textInfo.tl.getDescent();
				drawHeight = (int)(drawHeight - cellContentHeight + newHeight - desc) + 1; // 增加desc使得格子内容不紧凑
				cell.maxHeight = drawHeight;
			}
			
			cell.textDrawControl = TextControlEnum.DIRECT;
		}
	}
	
	//==================================================
	// Inner Classes
	//==================================================
	/**
	 * <code>ExcelWorkbook</code>封装97-2003,2007兼容操作的类.
	 */
	private static class ExcelWorkbook {
		
		private Workbook workbook;
		private DataFormatter formatter;
		private FormulaEvaluator evaluator;
		
		public ExcelWorkbook(Workbook workbook) {
			this.workbook = workbook;
			this.formatter = new DataFormatter();
			this.evaluator = createFormulaEvaluator();
		}
		
		public Sheet getSheet(String name) {
			return workbook.getSheet(name);
		}
		
		public FormulaEvaluator createFormulaEvaluator() {
			return workbook.getCreationHelper().createFormulaEvaluator();
		}
		
		/**
		 * 提取单元格的文本内容
		 * @see com.yss.sofa.report.engine#getExcelCellFormattedValue
		 * @param cell
		 * @return
		 */
		public String getExcelCellFormattedValue(ResultCell rc, Cell cell) {
			if (cell == null) {
				return "";
			}
			return (rc != null && rc.getDataType() == Const.CELL_DATA_TYPE_DATE) ? 
					formatter.formatCellValue(cell, evaluator).replaceAll("\"","") : formatter.formatCellValue(cell, evaluator);
		}
	}
	
	/**
	 * <code>PageEvent</code>页面事件接口.
	 */
	interface PageEvent {
		public void onStartPage(PictureWriter writer);
		public void onEndPage(PictureWriter writer);
	}
	
	/**
	 * <code>PicturePHFEvent</code>控制页眉页脚的事件类. <br/>
	 * <p><blockquote><pre>
	 * <xml-fragment xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:main="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
     *     <main:oddHeader>&amp;L11&amp;C&amp;P--&amp;N--&amp;D--&amp;T--&amp;F--&amp;A&amp;R22</main:oddHeader>
     *     <main:oddFooter>&amp;C第 &amp;P 页，共 &amp;N 页</main:oddFooter>
	 * </xml-fragment>
	 * 
	 * &L 表示左边部分开始
	 * &C 表示中间部分开始
	 * &R 表示右边部分开始

	 * &P 表示 &[页码] 变数
	 * &N 表示 &[总页数] 变数,PDF处理需注意
	 * &D 表示 &[日期]
	 * &T 表示 &[时间]
	 * &F 表示 &[文件名]
	 * &A 表示 &[当前SHEET的标签名]
	 * &G 表示 &[图片] 图片不处理
	 * </pre></blockquote></p>
	 */
	static class PicturePHFEvent implements PageEvent {

		public static final String P = "&P";
		public static final String N = "&N";
		public static final String D = "&D";
		public static final String T = "&T";
		public static final String F = "&F";
		public static final String A = "&A";
		//public static final String G = "&G";

		int headerHeight;
		int footerHeight;
		PictureFont font;
		int pageNumber = 1;
		int pageTotalNumber = 1;

		List<PictureCell> header;
		List<PictureCell> footer;
		
		// Constant - &D,&T,&F,&A
		String dateConstString;
		String timeConstString;
		String fileNameConstString;
		String sheetNameConstString;
		
		/**
		 * 添加页眉页脚.<br/>
		 * 
		 * <w:sectPr>
		 *   <w:headerReference w:type="default" r:id="rId2"/>
		 *   <w:footerReference w:type="default" r:id="rId3"/>
		 * </w:sectPr>
		 * 
		 * @param writer
		 */
		public void createPHF(WriterWrapper writerWrapper) {
			PictureWriter writer = writerWrapper.writer;
			
			float maxPHFWidth = writer.getActualPageWidth() - writer.leftMargin() - writer.rightMargin();
			float[] relativeWidths = new float[]{ maxPHFWidth * 0.33f, maxPHFWidth * 0.34f, maxPHFWidth * 0.33f };
			
			// 页眉
			float xPos = writer.leftMargin();
			float yPos = writer.topMargin() - headerHeight;
			drawPictureTable(writer, relativeWidths, headerHeight, header, xPos, yPos);
			
			// 页脚
			yPos = writer.getActualPageHeight() - writer.bottomMargin();
			if (footer != null && footer.size() > 0) {
				for (PictureCell xcell : footer) {
					// 页脚垂直底端对齐
					xcell.verticalAlignment = HVAlignment.BOTTOM;
				}
			}
			drawPictureTable(writer, relativeWidths, footerHeight, footer, xPos, yPos);
		}
		
		private void drawPictureTable(PictureWriter writer, float[] relativeWidths, float height, List<PictureCell> list, float xPos, float yPos) {
			if (list == null || list.size() == 0) {
				return;
			}
			
			DrawRectangle rect = new DrawRectangle(xPos, yPos, relativeWidths[0], height);
			
			for (int i = 0, j = list.size(); i < j; ++i) {
				PictureCell xcell = list.get(i);
				String text = xcell.cellText;
				
				if (text != null && text.length() > 0) {
					String newText = replaceVar(text);
					xcell.cellText = newText;
					xcell.textControl = TextControlEnum.CLIP;
					xcell.textDrawControl = TextControlEnum.CLIP;
					PictureTextInfo.getPictureTextInfo(xcell, writer);
					PictureTextInfo.calculateTextHeight(xcell, rect.drawWidth - 2, true, writer); // 超过部分换行处理
					writer.writeTextForClipCell(xcell, rect);
				}
				
				if (i + 1 < j) {
    				rect.drawXPos += relativeWidths[i];
    				rect.drawWidth = relativeWidths[i + 1];
				}
			}
		}
		
		private String replaceVar(String text) {
			if (hasPageNubmer(text)) {
				text = text.replaceAll(P, Integer.toString(pageNumber));
			}
			if (hasPageTotalNubmer(text)) {
				text = text.replaceAll(N, Integer.toString(pageTotalNumber));
			}
			return text;
		}
		
		public void onStartPage(PictureWriter writer) {
		}

		public void onEndPage(PictureWriter writer) {
		}
		
		public static boolean hasPageNubmer(String text) {
			if (text == null || text.length() == 0) {
				return false;
			}
			return text.indexOf(P) > -1;
		}
		
		public static boolean hasPageTotalNubmer(String text) {
			if (text == null || text.length() == 0) {
				return false;
			}
			return text.indexOf(N) > -1;
		}
		
		public String renderConstString(String text) {
			if (text == null || text.length() == 0) {
				return "";
			}
			
			text = text.replaceAll(D, dateConstString);
			text = text.replaceAll(T, timeConstString);
			text = text.replaceAll(F, fileNameConstString);
			text = text.replaceAll(A, sheetNameConstString);
			
			return text;
		}
	}
	
	//==================================================
	// Picture Tool Classes
	//==================================================
	static class WriterWrapper {
		PictureWriter writer;
		public WriterWrapper(PictureWriter writer) {
			this.writer = writer;
		}
	}
	
	static class PictureWriter {
		BufferedImage bi;
		Graphics2D g2d;
		FontRenderContext frc;
		
		int pageWidth;
		int pageHeight;
		int topMargin;
		int leftMargin;
		int bottomMargin;
		int rightMargin;
		
		public PictureWriter(int width, int height, int topMargin, int leftMargin, int bottomMargin, int rightMargin) {
			bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			g2d = bi.createGraphics();
			frc = g2d.getFontRenderContext();
			
			// 绘制启动抗锯齿效果
			g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			
			g2d.setBackground(Color.WHITE);
			g2d.clearRect(0, 0, width, height);
			
			this.pageWidth = width;
			this.pageHeight = height;
			this.topMargin = topMargin;
			this.leftMargin = leftMargin;
			this.bottomMargin = bottomMargin;
			this.rightMargin = rightMargin;
		}
		
		public int getPageWidth() {
			return pageWidth;
		}
		
		public int getPageHeight() {
			return pageHeight;
		}
		
		/**
		 * 添加一页
		 */
		public void newPage() {
//			if (pageEvent != null) {
//				pageEvent.onEndPage(this);
//			}
//			
//			// new page
//		    P p = factory.createP();
//		    body.getContent().add(p);
//		    setPPrSpacingToZero(p).setSectPr(body.getSectPr());
//		    
//		    ++totalPageNumber;
//			if (pageEvent != null) {
//				pageEvent.onStartPage(this);
//			}
		}
		
		/**
		 * 保存image的内容到输出流.
		 * 
		 * @param out
		 * @param picType
		 */
		public void save(OutputStream out, PictureType picType) {
			close();

			String formatName = picType.name().toLowerCase(); // png
			try {
				ImageIO.write(bi, formatName, out);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public void close() {
			if (g2d == null) {
				return;
			}
			
			g2d.dispose(); // complete draw
		}
		
		public int getActualPageWidth() {
			return bi.getWidth();
		}
		
		public int getActualPageHeight() {
			return bi.getHeight();
		}
		
		public void writeCellToPage(PictureCell xcell, DrawRectangle rect, boolean isDrawWholeCell, PicturePageInfo pageInfo, PictureTableCellInfo cellInfo) throws Exception {
			
			// background, border
			writeBorderAndBackground(xcell, rect);
			
			// text
			writeText(xcell, rect, isDrawWholeCell);
			
			// image
			if (xcell.image != null && xcell.image.hasImage()) {
				writeImage(xcell, rect, cellInfo.imageZoom);
			}
		}
		
		public void writeFloatingToPage(PicturePageInfo pageInfo, Floating floating, float imageZoom) throws Exception {
			// 计算是否要裁剪图片
			PictureImage image = clipImage(floating, imageZoom);
			if (image == null) {
				return;
			}
			int w = (int)(image.getScaledWidth() * imageZoom);
			int h = (int)(image.getScaledHeight() * imageZoom);
			int x = round(remainderToFloat(floating.drawXPos, pageInfo.onePageFitWidth) + pageInfo.leftMargin);
			int y = round(remainderToFloat(floating.drawYPos, pageInfo.onePageFitHeight) + pageInfo.topMargin);
			
			ByteArrayInputStream bais = new ByteArrayInputStream(image.imgBytes);
			BufferedImage bi = ImageIO.read(bais);
			g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); // 图片使用精细绘制保证原始效果
			g2d.drawImage(bi, x, y, w, h, null);  // 宽高负数就是翻转图片
			g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT);
			bais.close();
		}
		
		private PictureImage clipImage(Floating floating, float imageZoom) throws Exception {
			PictureImage image = floating.cell.image;
			
			DrawRectangle rect = floating.rect;
			int w = (int)(image.getScaledWidth() * imageZoom);
			int h = (int)(image.getScaledHeight() * imageZoom);
			if (w <= rect.drawWidth && h <= rect.drawHeight) {
				return floating.cell.image;
			}
			
			// wh to actual px
			int offsetXPx = round((floating.drawXPos - floating.cell.xPos) * imageZoom);
			int offsetYPx = round((floating.drawYPos - floating.cell.yPos) * imageZoom);
			if (offsetXPx >= image.w || offsetYPx >= image.h) {  // px
				return null;
			}
			net.coobird.thumbnailator.geometry.Position position = new Position(offsetXPx, offsetYPx);
			
			int width = round(rect.drawWidth * imageZoom);
			int height = round(rect.drawHeight * imageZoom);
			if (width < 1) {
				width = 1;
			}
			if (height < 1) {
				height = 1;
			}
			image = clipImage(image, position, width, height);
			
			//zoomImage(image, imageZoom);
			
			return image;
		}

		private void writeImage(PictureCell xcell, DrawRectangle rect, float imageZoom) throws Exception {
			// 计算是否要裁剪图片
			PictureImage image = clipImage(xcell, rect, imageZoom);
			if (image == null) {
				return;
			}
			
			ByteArrayInputStream bais = new ByteArrayInputStream(image.imgBytes);
			BufferedImage bi = ImageIO.read(bais);
			
			// 避免单元格中图片压线显示
			int x = round(rect.drawXPos + rect.drawBorderWidthLeft);
			int y = round(rect.drawYPos + rect.drawBorderWidthTop);
			int w = round(rect.drawWidth - rect.drawBorderWidthLeft - rect.drawBorderWidthRight);
			int h = round(rect.drawHeight - rect.drawBorderWidthTop - rect.drawBorderWidthBottom);
			g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); // 图片使用精细绘制保证原始效果
			g2d.drawImage(bi, x, y, w, h, null);  // 宽高负数就是翻转图片
			g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT);
			bais.close();
		}
		
		private PictureImage clipImage(PictureCell xcell, DrawRectangle rect, float imageZoom) throws Exception {
			PictureImage image = xcell.image;
			
			int w = (int)(image.getScaledWidth() * imageZoom);
			int h = (int)(image.getScaledHeight() * imageZoom);
			if (w <= rect.drawWidth && h <= rect.drawHeight) {
				return xcell.image;
			}
			
			// wh to actual px
			int offsetXPx = round(xcell.drawCompleteWidthXPos * imageZoom);
			int offsetYPx = round(xcell.drawCompleteHeight * imageZoom);
			if (offsetXPx >= image.w || offsetYPx >= image.w) { // px
				return null;
			}
			net.coobird.thumbnailator.geometry.Position position = new Position(offsetXPx, offsetYPx);
			
			int width = round(rect.drawWidth * imageZoom);
			int height = round(rect.drawHeight * imageZoom);
			if (width < 1) {
				width = 1;
			}
			if (height < 1) {
				height = 1;
			}
			image = clipImage(image, position, width, height);
			
			//zoomImage(image, imageZoom);
			
			return image;
		}
		
		private PictureImage clipImage(PictureImage image, net.coobird.thumbnailator.geometry.Position position, int width, int height) throws Exception {
			ByteArrayOutputStream out = new ByteArrayOutputStream();

			byte[] b = image.getImgBytes();

			// 1 unit = 1/72 in, assume windows 96dpi = 1 in, then 1 px = 0.75 pt
			Thumbnails.of(new ByteArrayInputStream(b))
		        .sourceRegion(position, width, height)
		        .size(width, height)
		        //.outputQuality(1)
		        .toOutputStream(out);
			
			PictureImage newImage = new PictureImage(out.toByteArray());
			newImage.update(width, height);

			out.close();
			
			return newImage;
		}

		private void writeText(PictureCell xcell, DrawRectangle rect, boolean isDrawWholeCell) {
			String text = xcell.cellText;
			
			if (text == null || text.length() == 0) {
			    return;
			}
			
			if (xcell.textControl == null) {
				return;
			}
			
			switch (xcell.textControl) {
				case CLIP : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						writeTextForWholeCell(xcell, rect);
					} else {
						writeTextForClipCell(xcell, rect);
					}
					break;
				}
				case SHRINK : {
					if (isDrawWholeCell) {
						writeTextForWholeCell(xcell, rect);
					} else {
						writeTextForClipCell(xcell, rect);
					}
					break;
				}
				case STRETCH : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						writeTextForWholeCell(xcell, rect);
					} else {
						writeTextForClipCell(xcell, rect);
					}
					break;
				}
				case AUTOSIZE : {
					if (isDrawWholeCell && xcell.textDrawControl == TextControlEnum.DIRECT) {
						writeTextForWholeCell(xcell, rect);
					} else {
						writeTextForClipCell(xcell, rect);
					}
					break;
				}
				default : {
					writeTextForClipCell(xcell, rect);
					break;
				}
			}
		}

		private void writeBorderAndBackground(PictureCell xcell, DrawRectangle rect) {
			// 颜色填充都是满格, 只要先填充背景再画边框就行
			Color background = xcell.backgroundColor;
			if (background != null) {
				Stroke originalStroke = g2d.getStroke();
				Color originalColor = g2d.getColor();
				
				g2d.setColor(background);
				g2d.fill(new Rectangle2D.Float(rect.drawXPos, rect.drawYPos, rect.drawWidth, rect.drawHeight));

				g2d.setStroke(originalStroke);
				g2d.setColor(originalColor);
			}
			
			if (rect.hasBorder() && xcell.borderRoundRectangle != null) {
				
				BorderRoundRectangle borderRoundRectangle = xcell.borderRoundRectangle;
				
				BorderRoundRectangle drawBorder = new BorderRoundRectangle();
				
				if (rect.hasBorder(DrawRectangle.TOP) && borderRoundRectangle.topBorder != null) {
					PictureBorderStyle style = borderRoundRectangle.topBorder;
					drawBorder.topBorder = style;
					rect.drawBorderWidthTop = style.getBorderWidth();
				}
				
				if (rect.hasBorder(DrawRectangle.LEFT) && borderRoundRectangle.leftBorder != null) {
					PictureBorderStyle style = borderRoundRectangle.leftBorder;
					drawBorder.leftBorder = style;
					rect.drawBorderWidthLeft = style.getBorderWidth();
				}
				
				if (rect.hasBorder(DrawRectangle.RIGHT) && borderRoundRectangle.rightBorder != null) {
					PictureBorderStyle style = borderRoundRectangle.rightBorder;
					drawBorder.rightBorder = style;
					rect.drawBorderWidthRight = style.getBorderWidth();
				}
				
				if (rect.hasBorder(DrawRectangle.BOTTOM) && borderRoundRectangle.bottomBorder != null) {
					PictureBorderStyle style = borderRoundRectangle.bottomBorder;
					drawBorder.bottomBorder = style;
					rect.drawBorderWidthBottom = style.getBorderWidth();
				}
				
				drawBorder.cellLayout(xcell, rect, g2d);
			}
		}

		public void writeTextForWholeCell(PictureCell xcell, DrawRectangle rect) {
			String phrase = xcell.cellText;
			if (phrase == null || phrase.length() == 0) {
				return;
			}
			
			if (PictureTextInfo.getPictureTextInfo(xcell, this) == null) {
				return;
			}

			drawText(xcell, rect, null);
		}
		
		public void writeTextForClipCell(PictureCell xcell, DrawRectangle rect) {
			String phrase = xcell.cellText;
			if (phrase == null || phrase.length() == 0) {
				return;
			}
			
			if (PictureTextInfo.getPictureTextInfo(xcell, this) == null) {
				return;
			}
			
			// 中间切分暂时不考虑, 因为都是完整绘制
			float x = rect.drawXPos;
			float y = rect.drawYPos;
			
			Graphics2D g2 = (Graphics2D) g2d.create(round(x), round(y), 
			                                        (int)rect.drawWidth, (int)rect.drawHeight);
			
			// 绘制启动抗锯齿效果
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			
			rect.drawXPos = 0;
			rect.drawYPos = 0;
			drawText(xcell, rect, g2);
			
			g2.dispose();
			rect.drawXPos = x;
			rect.drawYPos = y;
		}
		
		// 支持中英混排
		private void drawText(PictureCell xcell, DrawRectangle rect, Graphics2D g2) {
			PictureTextInfo textInfo = xcell.textInfo;
			if (textInfo == null || !textInfo.hasText()) {
				return;
			}
			
			Graphics2D g2d = (g2 != null ? g2 : this.g2d);
			FontRenderContext frc = this.frc;
			
			Stroke originalStroke = g2d.getStroke();
			Color originalColor = g2d.getColor();
			
			PictureFont pf = textInfo.font;
			float fitLeading = PictureTextInfo.getFitLeading(pf.fontSize);
			if (pf.color != null) {
				g2d.setColor(pf.color);
			}
			float emptyLineHeight = textInfo.emptyLineHeight;
			float textHeight = 0;
			
			boolean isStart = xcell.drawCompleteWidth == 0;
			
			float x = rect.drawXPos;
			float y = rect.drawYPos;
			// 垂直对齐设置
			if (xcell.verticalAlignment != null) {
				switch (xcell.verticalAlignment) {
					case MIDDLE : {
						y += (rect.drawHeight - textInfo.height)/2f;
						y = y < 0 ? rect.drawYPos : y;
						break;
					}
					case BOTTOM : {
						y += (rect.drawHeight - textInfo.height);
						y = y < 0 ? rect.drawYPos : y;
						break;
					}
					default : {
						// TOP
						break;
					}
				}
			}
			
			float drawXPos = x;
			float drawYPos = y;
			for (Chunk chunk : textInfo.chunkList) {
				if (chunk != null) {
					
					// Zero length string passed to TextLayout constructor.
					TextLayout textLayout = new TextLayout(chunk.as.getIterator(), frc);
					float textWidth = chunk.width; //textLayout.getAdvance();
					textHeight = chunk.height;
					
    				// 每行水平对齐设置
    				if (xcell.horizontalAlignment != null) {
    					switch (xcell.horizontalAlignment) {
    						case CENTER : {
    							drawXPos += (rect.drawWidth - textWidth)/2f;
    							break;
    						}
    						case RIGHT : {
    							drawXPos += (rect.drawWidth - textWidth);
    							if (xcell.horizontalAlignment == HVAlignment.RIGHT) {
    								drawXPos -= rect.drawBorderWidthRight;
    							}
    							break;
    						}
    						default : {
    							// LEFT
    							if (isStart) {
    								drawXPos += (rect.drawBorderWidthLeft + 1);
    							}
    							break;
    						}
    					}
    				}
    				
    		        
    				textLayout.draw(g2d, drawXPos, drawYPos + chunk.asc);
    				
    				if (pf.isStrikethru() || pf.isUnderlined()) {
    			        // size:72pt=5px, min=1px, left/right 10px
    					float size = (pf.fontSize / 72f * 5);
    					if (size > 1) {
    						g2d.setStroke(new BasicStroke(size));
    					}
    					int offset = (int)(pf.fontSize / 72f * 10);
    					float urx = drawXPos + offset;
    					float llx = drawXPos + textWidth - offset;
    					
    					if (pf.isStrikethru()) {
    						float ury = drawYPos + (textHeight * 0.7f);
    						//g2d.drawLine(urx, ury, llx, ury); // 不能用float
    						g2d.draw(new Line2D.Float(urx, ury, llx, ury));
    					}
    					if (pf.isUnderlined()) {
    						float ury = drawYPos + textHeight;
    						g2d.draw(new Line2D.Float(urx, ury, llx, ury));
    					}
    				}
					
				} else {
					textHeight = emptyLineHeight;
				}
				
				y += (textHeight + fitLeading);
				
				drawXPos = x;
				drawYPos = y;
			}
			
			
			g2d.setStroke(originalStroke);
			g2d.setColor(originalColor);
		}
		
		public int topMargin() {
			return topMargin;
		}
		
		public int leftMargin() {
			return leftMargin;
		}
		
		public int bottomMargin() {
			return bottomMargin;
		}
		
		public int rightMargin() {
			return rightMargin;
		}
		
	}
	
	/**
	 * <code>PictureCell</code>是Picture的格子.
	 */
	//==================================================
	// Customize Picture Table,Row,Cell Classes
	//==================================================
	static class PictureCell {
		float xPos;
		float yPos;
		int rowIndex;
		int colIndex;
		int rowspan;
		int colspan;
		float maxHeight;
		float maxWidth;
		PictureImage image;
		String cellText;
		PictureFont cellFont;
		HVAlignment horizontalAlignment;
		HVAlignment verticalAlignment;
		Color backgroundColor;
		BorderRoundRectangle borderRoundRectangle;
		float topBorderWidth;
		float leftBorderWidth;
		float bottomBorderWidth;
		float rightBorderWidth;
		
		// 过长或过大的文本内容是否进行自动换行,缩放填充,自动截取
		PictureTextInfo textInfo;
		//FontSelector fontSelector;
		TextControlEnum textControl;
		TextControlEnum textDrawControl;
		
		float drawWidth;
		float drawHeight;
		float originalXPos;
		float originalYPos;
		float originalWidth;
		float originalHeight;
		
		// 绘制相关
		float rowSplitDrawXPos;
		float rowSplitDrawYPos;
		float colSplitDrawXPos;
		boolean hasRowSplit;
		boolean hasColSplit;
		boolean hasRowColSplit;
		float drawSplitWidth;
		float drawSplitHeight;
		float drawCompleteWidth;
		float drawCompleteHeight;
		float nextSplitPageNum;
		// 每次绘制格子的起始坐标,格子左上角为(0,0)
		float drawCompleteWidthXPos;
		
		float drawSplitHeightOffset; // 插入表头的切分偏移量
		float drawCompletePreHeight;
		boolean isHeader;
		
		public static PictureCell copyCell(PictureCell cell) {
			PictureCell newCell = new PictureCell();
			
			newCell.xPos = cell.xPos;
			newCell.yPos = cell.yPos;
			newCell.rowIndex = cell.rowIndex;
			newCell.colIndex = cell.colIndex;
			newCell.rowspan = cell.rowspan;
			newCell.colspan = cell.colspan;
			newCell.maxHeight = cell.maxHeight;
			newCell.maxWidth = cell.maxWidth;
			newCell.image = cell.image;
			newCell.cellText = cell.cellText;
			newCell.cellFont = cell.cellFont;
			newCell.horizontalAlignment = cell.horizontalAlignment;
			newCell.verticalAlignment = cell.verticalAlignment;
			newCell.backgroundColor = cell.backgroundColor;
			newCell.borderRoundRectangle = BorderRoundRectangle.copyBorder(cell.borderRoundRectangle); //cell.borderRoundRectangle;
			newCell.topBorderWidth = cell.topBorderWidth;
			newCell.leftBorderWidth = cell.leftBorderWidth;
			newCell.bottomBorderWidth = cell.bottomBorderWidth;
			newCell.rightBorderWidth = cell.rightBorderWidth;
			newCell.textInfo = cell.textInfo;
			//newCell.fontSelector = cell.fontSelector;
			newCell.textControl = cell.textControl;
			newCell.textDrawControl = cell.textDrawControl;
			newCell.drawWidth = cell.drawWidth;
			newCell.drawHeight = cell.drawHeight;
			newCell.originalXPos = cell.originalXPos;
			newCell.originalYPos = cell.originalYPos;
			newCell.originalWidth = cell.originalWidth;
			newCell.originalHeight = cell.originalHeight;
			
			newCell.rowSplitDrawXPos = cell.rowSplitDrawXPos;
			newCell.rowSplitDrawYPos = cell.rowSplitDrawYPos;
			newCell.colSplitDrawXPos = cell.colSplitDrawXPos;
			newCell.hasRowSplit = cell.hasRowSplit;
			newCell.hasColSplit = cell.hasColSplit;
			newCell.hasRowColSplit = cell.hasRowColSplit;
			newCell.drawSplitWidth = cell.drawSplitWidth;
			newCell.drawSplitHeight = cell.drawSplitHeight;
			newCell.drawCompleteWidth = cell.drawCompleteWidth;
			newCell.drawCompleteHeight = cell.drawCompleteHeight;
			newCell.nextSplitPageNum = cell.nextSplitPageNum;
			
			newCell.drawCompleteWidthXPos = cell.drawCompleteWidthXPos;
			
			newCell.drawSplitHeightOffset = cell.drawSplitHeightOffset;
			newCell.drawCompletePreHeight = cell.drawCompletePreHeight;
			newCell.isHeader = cell.isHeader;
			
			return newCell;
		}

		public String toString() {
			return this.cellText != null ? this.cellText : "";
		}
		
	}
	
	//==================================================
	// Picture Floating Classes
	//==================================================
	static class PictureImage {
		int w; // Picture Image size unit is Pixel
		int h;
		byte[] imgBytes;
		
		public PictureImage(byte[] imgBytes) throws IOException {
			this.imgBytes = imgBytes;
		}
		
		/**
		 * 更新图片宽高信息
		 * @param width the width unit is pixel
		 * @param height the height unit is pixel
		 */
		public void update(int width, int height) {
			w = width;
			h = height;
		}

		public byte[] getImgBytes() {
			return imgBytes;
		}
		
		public boolean hasImage() {
			return imgBytes != null && imgBytes.length > 0;
		}
		
		public int getScaledWidth() {
			return w;
		}
		
		public int getScaledHeight() {
			return h;
		}
	}
	
	//==================================================
	// Picture Font Classes
	//==================================================
	static class PictureFont {
		String fontName;
		int fontSize;
		int fontStyle;
		Color color;
		
		/** this is a possible style. */
		public static final int NORMAL = 0;

		/** this is a possible style. */
		public static final int BOLD = 1;

		/** this is a possible style. */
		public static final int ITALIC = 2;

		/** this is a possible style. */
		public static final int UNDERLINE = 4;

		/** this is a possible style. */
		public static final int STRIKETHRU = 8;

		/** this is a possible style. */
		public static final int BOLDITALIC = BOLD | ITALIC;

		public PictureFont() {
		}
		
		public PictureFont(String fontName, int fontSize, int fontStyle, Color color) {
			this.fontName = fontName;
			this.fontSize = fontSize;
			this.fontStyle = fontStyle;
			this.color = color;
		}
		
		public Font getFitFont() {
			int style = NORMAL;
			if (hasFontStyle()) {
				if (isBold()) {
					style |= BOLD;
				}
				if (isItalic()) {
					style |= ITALIC;
				}
			}
			Font font = new Font(this.fontName, style, ptToPx(fontSize));
			return font;
		}
		
		/**
		 * checks if this font is Bold.
		 *
		 * @return a <CODE>boolean</CODE>
		 */
		public boolean isBold() {
			if (fontStyle == NORMAL) {
				return false;
			}
			return (fontStyle & BOLD) == BOLD;
		}

		/**
		 * checks if this font is italic.
		 *
		 * @return a <CODE>boolean</CODE>
		 */
		public boolean isItalic() {
			if (fontStyle == NORMAL) {
				return false;
			}
			return (fontStyle & ITALIC) == ITALIC;
		}

		/**
		 * checks if this font is underlined.
		 *
		 * @return a <CODE>boolean</CODE>
		 */
		public boolean isUnderlined() {
			if (fontStyle == NORMAL) {
				return false;
			}
			return (fontStyle & UNDERLINE) == UNDERLINE;
		}

		/**
		 * checks if the style of this font is STRIKETHRU.
		 *
		 * @return a <CODE>boolean</CODE>
		 */
		public boolean isStrikethru() {
			if (fontStyle == NORMAL) {
				return false;
			}
			return (fontStyle & STRIKETHRU) == STRIKETHRU;
		}

		public boolean hasFontStyle() {
			return fontStyle > 0;
		}
		
	}
	
	/**
	 * <code>FontFactory</code>字体工厂类.
	 */
	static final class FontFactory {
		private static FontFactoryImp fontImp = new FontFactoryImp();
		
		private FontFactory() {
		}
		
		/**
		 * 获得物理字体对应的实例.
		 * @param fontName 字体名称.例如:华文彩云字体，名字使用STCaiyun或华文彩云都可以, 不区分大小写, 内部都转小写对应上
		 * @return
		 */
		public static String getFont(String fontFamily, String fontName) {
			return fontImp.getFont(fontFamily, fontName);
		}
		
		/**
		 * 获得默认的字体, 以便计算字体宽度.
		 * @return
		 */
		public static String getDefaultFont() {
			return fontImp.getDefaultFont();
		}
	}
	
	/**
	 * <code>FontFactoryImp</code>字体获得具体实现类.
	 */
	static class FontFactoryImp {
		private static final Map<String, Font> LOAD_PHYSICAL_FONT_MAPPING = new HashMap<String, Font>();
		private static final Set<String> DEFAULT_LOGIC_FONT_SET = new HashSet<String>(5);
		private static final Set<String> OS_PHYSICAL_FONT_SET = new HashSet<String>();
		private static Locale systemLocale;
		
		static {
			DEFAULT_LOGIC_FONT_SET.add("Serif");
			DEFAULT_LOGIC_FONT_SET.add("SansSerif");
			DEFAULT_LOGIC_FONT_SET.add("Monospaced");
			DEFAULT_LOGIC_FONT_SET.add("Dialog");
			DEFAULT_LOGIC_FONT_SET.add("DialogInput");
			
			// sun.awt.Win32GraphicsEnvironment / sun.java2d.SunGraphicsEnvironment
			GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
			String[] fontNames = ge.getAvailableFontFamilyNames();
			for (int i = 0; i < fontNames.length; ++i) {
				OS_PHYSICAL_FONT_SET.add(fontNames[i]);
			}
			
			systemLocale = initSystemLocale();
		}
		
		private static Locale initSystemLocale() {
			// 使用注册时,内部代码使用的是这个Locale
	        String str1 = System.getProperty("file.encoding", "");
	        String str2 = System.getProperty("sun.jnu.encoding");
	        if ((str2 != null) && (!str2.equals(str1))) {
	        	return Locale.ROOT;
	        }
	        String str3 = System.getProperty("user.language", "en");
	        String str4 = System.getProperty("user.country", "");
	        String str5 = System.getProperty("user.variant", "");
	        return new Locale(str3, str4, str5);
		}
		
		public FontFactoryImp(){
			// OS字体JRE自动加载
			registerCustomDirectories();
		}
		
		public String getFont(String fontFamily, String fontName) {
			if (DEFAULT_LOGIC_FONT_SET.contains(fontFamily) && 
					!fontFamily.equals(fontName)) {
				// try custom font
				Font registerFont = LOAD_PHYSICAL_FONT_MAPPING.get(fontName);
				if (registerFont != null) {
					fontName = registerFont.getName();
				}
			}
			return fontName;
		}
		
		public String getDefaultFont() {
			return FONT_DEFAULT_CHINESE;
		}
		
		private void registerCustomDirectories() {
			// from other
			try {
				// SOFA fonts
				try {
					registerDirectory(SOFAEnvionmentUtil.getSOFAHomePath() + "/Fonts");
				} catch (Exception e) {
				}
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		private void registerDirectory(String fontPath) {
			if (fontPath == null || fontPath.length() == 0) {
				return;
			}
			
			File file = new File(fontPath);
			if (file.isFile()) {
				registerFont(file);
			} else if (file.isDirectory()) {
				File[] files = file.listFiles(new FilenameFilter() {
					public boolean accept(File dir, String name) {
						if (dir.isFile()) {
							int idx = name.lastIndexOf(".");
							if (idx > -1) {
								String suffix = name.substring(idx).toLowerCase();
								return suffix.equals(".ttf") || suffix.equals(".ttc");
							}
						}
						return false;
					}
				});
				for (int i = 0; i < files.length; ++i) {
					registerFont(files[i]);
				}
			}
		}
		
		private void registerFont(File file) {
			Font font = createFont(file);
			if (font != null && !OS_PHYSICAL_FONT_SET.contains(font.getFamily())) {
				GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
				String registerFontFamily = font.getFamily(systemLocale);
				Font registerFont = new Font(registerFontFamily, Font.PLAIN, 1);
				String localLocaleFamily = font.getFamily();
				LOAD_PHYSICAL_FONT_MAPPING.put(localLocaleFamily, registerFont);  // 华文彩云
				LOAD_PHYSICAL_FONT_MAPPING.put(registerFontFamily, registerFont); // STCaiyun
			}
		}
		
		private Font createFont(File fontFile) {
			Font loadFont = null;
			try {
				loadFont = Font.createFont(Font.TRUETYPE_FONT, fontFile);
			} catch (FontFormatException e) {
				e.printStackTrace();
				
				try {
					loadFont = Font.createFont(Font.TYPE1_FONT, fontFile);
				} catch (FontFormatException e1) {
					e1.printStackTrace();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			return loadFont;
		}
		
	}
	
	//==================================================
	// Picture Cells Information Classes
	//==================================================
	/**
	 * <code>PictureCellInfo</code>是Picture表格中所有格子的信息
	 */
	static class PictureTableCellInfo {
		/**
		 * 标题部分. 第一页开始部分.
		 */
		List<PictureCell> title;
		/**
		 * 顶端标题行. 第一页title下, 之后每页的开头(Excel页面设置->工作表) 对相应 fixed-header渲染器.
		 * fixed-header渲染器会让报表具有固定的表头，报表内容在竖向滚动时，表头不会随着内容滚动，类似于 excel中的冻结行。
		 * 在中模板需要配置 page 单元格的 title 和 header 属性。 title 和 header 属性分别表示报表的表头行数和列头
		 * 行数——注意是行数不是行号。 如 title=3， header=4，就表示报表的第 1 行~第 3 行是表头，第 4 行~第 7 行是列头，那么在 fixed-header 渲染器中，报表的前 7 行（ 3+4）都会被固定。
		 * 
		 * 在 sheet[main]中，结束符##现在可配置属性， ##{"title":2,"header":1}，
		 * 表示前 2 行为表头（只第 1页显示），第 3 行为列头（每页都显示），会影响到打印的结果，对预览结果无影响，若不配置，则全部作为明细数据；
		 */
		List<PictureCell> header;
		/**
		 * 表格中包含的主要格子<code>PictureCell</code>的集合
		 */
		List<PictureCell> detail;
		/**
		 * 最后一页最后一行下方的内容
		 */
		List<PictureCell> footer; //这个可忽略
		/**
		 * 浮动的对象.例如固定位置的二维码图片
		 */
		List<PictureCell> floating;
		/**
		 * 绘制使用的集合
		 */
		List<PictureCell> cells;
		/**
		 * 每个colspan对应的宽度
		 */
		int[] relativeWidths;
		/**
		 * 表格的宽度
		 */
		int totalWidth;
		/**
		 * 表格高度
		 */
		int totalHeight;
		/**
		 * 格子集合中每个格子的Y轴坐标.从左上角计算.
		 */
		Map<Integer, PictureCellPos> rowIndexPosMapping;
		/**
		 * 格子集合中每个格子的X轴坐标.从左上角计算.
		 */
		Map<Integer, PictureCellPos> colIndexPosMapping;
		/**
		 * 一页的高度
		 */
		int onePageFitHeight; // 计算使用: pageHeight - topMargin - bottomMargin;
		/**
		 * 格子的缩放比例
		 */
		float cellZoom;
		/**
		 * 图片的缩放比例
		 */
		float imageZoom;
		/**
		 * 字体的缩放比例
		 */
		float fontZoom;
		/**
		 * 行数
		 */
		int rowCount;
		/**
		 * 列数
		 */
		int colCount;
		
		/**
		 * 表头信息(可用于动态构建表头)
		 */
		HeaderInfo headerInfo;
		
		/**
		 * 构造函数. 所有格子集合应该按rowIndex和colIndex有序保存
		 * @param title 标题
		 * @param header 页眉
		 * @param detail 内容
		 * @param footer 页脚
		 * @param floating 浮动对象
		 * @param relativeWidths 格子的colspan对应的宽度
		 * @param strategy 处理格子信息的策略
		 */
		public PictureTableCellInfo(List<PictureCell> title,
		                        List<PictureCell> header,
		                        List<PictureCell> detail, 
		                        List<PictureCell> footer,
		                        List<PictureCell> floating,
		                        int[] relativeWidths,
		                        DrawStrategy strategy) {
			this.title = title;
			this.header = header;
			this.detail = detail;
			this.footer = footer;
			this.floating = floating;
			this.cells = new ArrayList<PictureCell>();
			this.relativeWidths = relativeWidths;
			this.totalWidth = calculateTotalWidth();
			this.onePageFitHeight = strategy.getDrawBodyHeight();
			this.rowIndexPosMapping = new HashMap<Integer, PictureCellPos>();
			this.colIndexPosMapping = new HashMap<Integer, PictureCellPos>();

			strategy.calculate(this);
		}

		/**
		 * 计算表格的宽度
		 * @return
		 */
		public int calculateTotalWidth() {
			if (relativeWidths == null || relativeWidths.length == 0) {
				return 0;
			}
			int width = 0;
			for (int w : relativeWidths) {
				width += w;
			}
			return width;
		}
		
		/**
		 * 通过格子的colIndex找到格子的原始X坐标
		 * @param cell_
		 * @return
		 */
		public float calculateXPos(PictureCell cell_) {
			if (cell_.colIndex == 0) {
				return 0;
			}
			
			PictureCellPos pos = colIndexPosMapping.get(Integer.valueOf(cell_.colIndex));
			if (pos != null) {
				return pos.xPos;
			}
			
			int xPos = relativeWidths[0];
			for (int i = 1, j = cell_.colIndex; i < j; i++) {
				xPos += relativeWidths[i];
			}
			
			return xPos;
		}
		
		/**
		 * 通过colIndex和colspan获得格子的宽度
		 * @param colIndex 格子的列索引
		 * @param colspan 格子占用的列数
		 * @return
		 */
		public int getCellWidth(int colIndex, int colspan) {
			int width = relativeWidths[colIndex];
			for (int i = 1; i < colspan; i++) {
				width += relativeWidths[colIndex + i];
			}
			return width;
		}
	}
	
	/**
	 * <code>PictureCellPos</code>是Picture表格中格子的坐标信息
	 */
	static class PictureCellPos {
		PictureCell cell;
		float xPos;
		float yPos;
		float width;
		float height;
		float[] heightArray;
	}
	
	/**
	 * <code>HeaderInfo</code>是表头信息.用于构造动态表头
	 */
	static class HeaderInfo {
		int headerRowspan;
		int headerHeight;
		Map<Integer, List<PictureCell>> pageNumHeaderMapping;
	}
	
	/**
	 * <code>PictureTextInfo</code>是文本内容绘制信息
	 */
	static class PictureTextInfo {
		String phrase;
		List<String> chunks;
		PictureFont font;
		Font fitFont;
		float width; // Return the width unit is Pixel
		float height;
		
		// 中英混排使用
		List<Chunk> chunkList;
		float emptyLineHeight;

		public PictureTextInfo(String phrase, PictureFont font, PictureWriter writer) {
			// 支持中英混排
			this.phrase = phrase;
			this.font = font;
			this.fitFont = font.getFitFont();
			float fitLeading = getFitLeading(font.fontSize);
			LineMetrics lm = fitFont.getLineMetrics("", writer.frc);
			this.emptyLineHeight = lm.getAscent() + lm.getDescent() + fitLeading;
			
			if (hasText()) {
				
				float maxWidth = 0;
				float maxHeight = 0;
				List<Chunk> chunkList = new ArrayList<Chunk>();
				int beginIndex = 0;
				int endIndex = 0;
				int currentIndex = 0;
				do {
					if ((endIndex = phrase.indexOf('\n', currentIndex)) > -1) {
						String subStr = phrase.substring(beginIndex, endIndex);
						Chunk c = getChunk(subStr, fitFont, writer);
						chunkList.add(c);
						if (c != null) {
							if (maxWidth < c.width) {
								maxWidth = c.width;
							}
							maxHeight += (c.height + fitLeading);
						} else {
							maxHeight += emptyLineHeight;
						}
						
						currentIndex = endIndex + 1;
						beginIndex = currentIndex;
					} else {
					    break;
				    }
			    } while(true);
				
				if (beginIndex == 0 && endIndex == -1) {
					Chunk c = getChunk(phrase, fitFont, writer);
					chunkList.add(c);
					if (c != null) {
						if (maxWidth < c.width) {
							maxWidth = c.width;
						}
						maxHeight += (c.height + fitLeading);
					} else {
						maxHeight += emptyLineHeight;
					}
				} else if (beginIndex < phrase.length()) {
					String subStr = phrase.substring(beginIndex);
					Chunk c = getChunk(subStr, fitFont, writer);
					chunkList.add(c);
					if (c != null) {
						if (maxWidth < c.width) {
							maxWidth = c.width;
						}
						maxHeight += (c.height + fitLeading);
					} else {
						maxHeight += emptyLineHeight;
					}
				}
				
				this.chunkList = chunkList;
				this.width = maxWidth;
				this.height = maxHeight;
				
			} else {
				height = emptyLineHeight;
			}
		}
		
		private Chunk getChunk(String text, Font font, PictureWriter writer) {
			if (text == null || text.length() == 0) {
				return null;
			}
			
			Chunk result = new Chunk();
			result.text = text;
			
			AttributedString as = new AttributedString(text);
			StringCharacterIterator sci = new StringCharacterIterator(text);
			int limit = text.length();
			int index = font.canDisplayUpTo(sci, 0, limit);
			if (index == -1) {
				as.addAttribute(TextAttribute.FONT, font, 0, limit);
				result.firstFont = font;
				result.as = as;
				TextLayout tl = new TextLayout(text, result.firstFont, writer.frc);
				result.width = tl.getAdvance();
				result.height = tl.getAscent() + tl.getDescent();
				result.asc = tl.getAscent();
				result.desc = tl.getDescent();
				return result;
			} else {
				Font selectorFont = new Font(FONT_DEFAULT_CHINESE, font.getStyle(), font.getSize());
				if (limit == 1) {
					as.addAttribute(TextAttribute.FONT, selectorFont, 0, 1);
					result.firstFont = selectorFont;
					result.as = as;
					TextLayout tl = new TextLayout(text, result.firstFont, writer.frc);
					result.width = tl.getAdvance();
					result.height = tl.getAscent() + tl.getDescent();
					result.asc = tl.getAscent();
					result.desc = tl.getDescent();
					return result;
				}
				
				StringBuilder defaultFontStr = new StringBuilder();
				StringBuilder replaceFontStr = new StringBuilder();
				
				int beginIndex = 0;
				int endIndex = index;
				if (index > 0) {
    				as.addAttribute(TextAttribute.FONT, font, beginIndex, endIndex);
    				defaultFontStr.append(text.substring(beginIndex, endIndex));
    				result.firstFont = font;
				} else {
					as.addAttribute(TextAttribute.FONT, selectorFont, 0, 1);
					replaceFontStr.append(text.charAt(0));
					result.firstFont = selectorFont;
					++endIndex;
				}
				
				do {
					beginIndex = endIndex;
					endIndex = font.canDisplayUpTo(sci, beginIndex, limit);
					
					if (endIndex == -1) {
						as.addAttribute(TextAttribute.FONT, font, beginIndex, limit);
						defaultFontStr.append(text.substring(beginIndex));
						break;
					} else {
						if (beginIndex < endIndex) {
							as.addAttribute(TextAttribute.FONT, font, beginIndex, endIndex);
							defaultFontStr.append(text.substring(beginIndex, endIndex));
							beginIndex = endIndex;
						}
						as.addAttribute(TextAttribute.FONT, selectorFont, beginIndex, endIndex + 1);
						replaceFontStr.append(text.substring(beginIndex, endIndex + 1));

					}
				} while (++endIndex < limit);
				
				result.as = as;
				float width = 0;
				float height = 0;
				float asc = 0;
				float desc = 0;
				if (defaultFontStr.length() > 0) {
					TextLayout tl = new TextLayout(defaultFontStr.toString(), font, writer.frc);
					width = tl.getAdvance();
					height = tl.getAscent() + tl.getDescent();
					asc = tl.getAscent();
					desc = tl.getDescent();
				}
				if (replaceFontStr.length() > 0) {
					TextLayout tl = new TextLayout(replaceFontStr.toString(), selectorFont, writer.frc);
					width += tl.getAdvance();
					float height2 = tl.getAscent() + tl.getDescent();
					if (height < height2) {
						height = height2;
					}
					float asc2 = tl.getAscent();
					if (asc < asc2) {
						asc = asc2;
					}
					float desc2 = tl.getDescent();
					if (desc < desc2) {
						desc = desc2;
					}
				}
				result.width = width;
				result.height = height;
				result.asc = asc;
				result.desc = desc;
			}
			
			return result;
		}
		
		static class Chunk {
			String text;
			Font firstFont;
			AttributedString as;
			float width;
			float height;
			float asc;  // 行中最大的文字的数据
			float desc;
		}
		
		public boolean hasText() {
			return phrase != null && phrase.length() > 0;
		}
		
		/**
		 * 获得适合的行间距. TextLayout默认给的通常都太大
		 * @param l
		 * @return
		 */
		public static float getFitLeading(float l) {
			// int fitLeading = Math.round(fontSize * 1.05f); // 测试"很"字
			float fontSize = l * 1.05f;
			fontSize = fontSize < 10 ? (int)(fontSize + 1) : fontSize;
			return Math.round(fontSize - l);
		}
		
		public static PictureTextInfo getPictureTextInfo(PictureCell xcell, PictureWriter writer) {
			PictureTextInfo textInfo = xcell.textInfo;
			if (textInfo == null && xcell.cellFont != null) {
				String phrase = xcell.cellText;
				if (phrase == null) {
					phrase = "";
				}
				textInfo = new PictureTextInfo(phrase, xcell.cellFont, writer);
				xcell.textInfo = textInfo;
			}
			return textInfo;
		}
		
		/**
		 * 给定宽度计算指定文本内容需要的高度
		 * @param textInfo
		 * @param phrase
		 * @param limitWidth
		 * @return
		 */
		public static float calculateTextHeight(PictureCell xcell, float limitWidth, boolean isChangeChunks, PictureWriter writer) {
			// 支持中英混排
			PictureTextInfo textInfo = xcell.textInfo;
			if (textInfo.hasText() && textInfo.width > limitWidth) {
				// 有变化才换
				boolean hasChange = false;
				StringBuilder list = new StringBuilder();
				
				StringBuilder stringBuilder = new StringBuilder();
				float currentWidth = 0;
				int startOffset = 0;
				int endOffset = 0;
				
				List<Chunk> chunkList = textInfo.chunkList;
				for (int i = 0, size = chunkList.size(); i < size; ++i) {
					Chunk chunk = chunkList.get(i);
					if (chunk != null && chunk.text.length() > 0) {
						
						String lineStr = chunk.text;
						TextLayout tl = new TextLayout(chunk.as.getIterator(), writer.frc);
						startOffset = 0;
						endOffset = lineStr.length();
						
						for (int j = 0; startOffset < endOffset; ++startOffset, ++j) {
							char c = lineStr.charAt(j);
							if (c == '\n') { // 正常不会有回车,这里任然判断
								list.append(stringBuilder.toString()).append('\n');
								currentWidth = 0;
								stringBuilder.delete(0, stringBuilder.length());
								continue;
							}
							
							float[] cInfoLeading = tl.getCaretInfo(TextHitInfo.leading(startOffset));
							float[] cInfoTrailing = tl.getCaretInfo(TextHitInfo.trailing(startOffset));
							float onePictureWidth = (cInfoTrailing[0] - cInfoLeading[0]);
							
							if ( (currentWidth + onePictureWidth) > limitWidth ) {
								hasChange = true;
								list.append(stringBuilder.toString()).append('\n');
								currentWidth = 0;
								stringBuilder.delete(0, stringBuilder.length());
							}
							currentWidth += onePictureWidth;
							stringBuilder.append(c);
						}
					} 
					// 原来的回车
					list.append(stringBuilder.toString()).append('\n');
					stringBuilder.delete(0, stringBuilder.length());
					currentWidth = 0;
				}
				
				PictureTextInfo newTextInfo = new PictureTextInfo(list.toString(), textInfo.font, writer);
				float newTextHeight = newTextInfo.height;
				
				if (hasChange && isChangeChunks) {
					xcell.textInfo = newTextInfo;
				}
				
				return newTextHeight;
				
			} else {
				return textInfo.height;
			}
			
		}
		
		public float getPictureTextWidth() {
			return width;
		}
		
		public float getPictureTextHeight() {
			return height;
		}
	}
	
	/**
	 * <code>PicturePageInfo</code>是Picture页面信息
	 */
	static class PicturePageInfo {
		
		PictureWriter writer;
		float pageWidth;
		float pageHeight;
		float topMargin;
		float leftMargin;
		float bottomMargin;
		float rightMargin;
		float pageFitWidth;
		float pageFitHeight;
		float onePageFitWidth;
		float onePageFitHeight;
		
		float pageMaxFitHeight; // 最后一页时的内容高度
    	int pageWidthNum;
    	int pageHeightNum;
    	int pageHeightSplitNum;
    	Map<Integer, PicturePageInfo> canvasMapping;
    	
		float pageSplitHeight;
		float pageSplitHeightOffset;
		
		private PicturePageInfo() {
		}
		
		private PicturePageInfo(PictureWriter writer, float pageSplitHeight) {
			this.writer = writer;

			this.pageWidth = writer.getPageWidth();
			this.pageHeight = writer.getPageHeight();
			this.topMargin = writer.topMargin();
    		this.leftMargin = writer.leftMargin();
    		this.bottomMargin = writer.bottomMargin();
    		this.rightMargin = writer.rightMargin();
    		this.pageFitWidth = pageWidth - rightMargin;
    		this.pageFitHeight = pageHeight - bottomMargin;
    		this.onePageFitWidth = pageWidth - leftMargin - rightMargin;
    		this.onePageFitHeight = pageHeight - topMargin - bottomMargin;
    		
    		this.pageMaxFitHeight = pageFitHeight;
    		this.pageWidthNum = 1;
    		this.pageHeightNum = 1;
    		this.pageHeightSplitNum = 1;
    		
    		this.canvasMapping = new HashMap<Integer, PicturePageInfo>();
    		this.canvasMapping.put(Integer.valueOf(pageHeightNum), this);
    		
			this.pageSplitHeight = (pageSplitHeight > onePageFitHeight || pageSplitHeight < 1 ? onePageFitHeight : pageSplitHeight);
			pageSplitHeightOffset = this.onePageFitHeight - this.pageSplitHeight;
		}
		
    	/**
    	 * 获得实际要绘制的X坐标
    	 * @param pageNum 页码
    	 * @param xPos 绘制的X坐标
    	 * @return
    	 */
		public float transferToPageXPos(Integer pageNum, float xPos) {
			PicturePageInfo pageInfo = canvasMapping.get(pageNum);
			if (pageInfo == null) {
				return -1;
			}
			
			// 使用整数计算避免浮点计算误差
			int currentMaxWith = (int) (pageInfo.onePageFitWidth * pageInfo.pageWidthNum + 0.1f);
			int intXPos = (int) (xPos + 0.1f);
			if (intXPos > currentMaxWith) {
				return -1;
			} else if (currentMaxWith - intXPos > (int)(pageInfo.onePageFitWidth + 1)) { // 493 - 0 < 492.95
				return -2;
			}
			
			float result = pageInfo.leftMargin;
			if (intXPos == currentMaxWith) {
				result += pageInfo.onePageFitWidth;
			} else if (currentMaxWith - intXPos < (int)pageInfo.onePageFitWidth) {
				float mod = remainderToFloat(xPos + 0.1f, pageInfo.onePageFitWidth);
				if (mod < 1) {
					mod = 0;
    			} else {
    				mod -= 0.1f;
    			}
				result += mod;
			}
			
			return toRoundHalfUpFloat(result, 2);  // 避免490.8999 < 490.90
		}
    	
		/**
		 * 获得实际要绘制的Y坐标
		 * @param pageNum 页码
		 * @param yPos 绘制的Y坐标
		 * @return
		 */
    	public float transferToPageYPos(Integer pageNum, float yPos) {
    		PicturePageInfo pageInfo = canvasMapping.get(pageNum);
			if (pageInfo == null) {
				return -1;
			}
			
			// 使用整数计算避免浮点计算误差
			int currentMaxHeight = (int) (pageInfo.onePageFitHeight * pageInfo.pageHeightNum + 0.1);
			int intYPos = (int) (yPos + 0.1);
			if (intYPos > currentMaxHeight) {
			    return -1;
		    } else if (currentMaxHeight - intYPos > (int)(pageInfo.onePageFitHeight + 1)) {
			    return -2;
		    }
			
			float result = pageInfo.topMargin; // bottomMargin
    		if (intYPos == currentMaxHeight) {
    			result += pageInfo.onePageFitHeight;
    		} else if (currentMaxHeight - intYPos >= (int)pageInfo.onePageFitHeight) {
    			
    		} else {
    			float mod = remainderToFloat(yPos + 0.1f, pageInfo.onePageFitHeight);
    			if (mod < 1) {
    				mod = 0;
    			} else {
    				mod -= 0.1f;
    			}
    			result += mod;
    		}
    		
    		return toRoundHalfUpFloat(result, 2);  // 避免490.8999 < 490.90
    	}
    	
    	/**
    	 * 获得Y坐标对应的实际页面底部的适合距离
    	 * @param yPos
    	 * @return
    	 */
    	public float transferToFitYPos(float yPos) {
    		float yPosFloat = yPos + 0.1f;
    		int yPosInt = (int) yPosFloat;
    		int onePageFitHeightInt = (int) (onePageFitHeight + 0.1f);
    		if (yPosInt == onePageFitHeightInt) {
    			return toRoundHalfUpFloat(topMargin + onePageFitHeight, 2);
    		}
    		if (yPosInt < onePageFitHeightInt) {
    			return toRoundHalfUpFloat(topMargin + yPos, 2);
    		}
    		
    		int n = (int) ((yPosFloat) / onePageFitHeight);
    		float m = remainderToFloat(yPosFloat, onePageFitHeight); // 避免1460.4 % 486.80002 = 486.79996
    		float result = pageHeight * n - bottomMargin;
    		if (m - 0.1f >= 1) {
    			result += (bottomMargin + topMargin + m - 0.1f);
    		}
    		return toRoundHalfUpFloat(result, 2);  // 避免490.8999 < 490.90
    	}
    	
        /**
         * 通过页码获得对应的Picture页面信息
         * @param pageNum
         * @return
         */
        public PicturePageInfo getPageInfo(Integer pageNum) {
        	return canvasMapping.get(pageNum);
        }
        
    	/**
    	 * 新建一页Picture
    	 * @return
    	 */
    	public PicturePageInfo newPage() {
    		this.pageFitHeight += this.pageHeight;
    		this.pageMaxFitHeight = this.pageFitHeight;
    		++pageHeightNum;
    		++pageHeightSplitNum;
    		this.writer.newPage();
    		
    		PicturePageInfo pageInfo = createPageInfo();
    		canvasMapping.put(Integer.valueOf(pageInfo.pageHeightNum), pageInfo);
    		
    		return pageInfo;
    	}
    	
    	private PicturePageInfo createPageInfo() {
    		PicturePageInfo pageInfo = new PicturePageInfo();
    		
    		pageInfo.writer = writer;

    		pageInfo.pageWidth = writer.getPageWidth();
    		pageInfo.pageHeight = writer.getPageHeight();
    		pageInfo.topMargin = writer.topMargin();
    		pageInfo.leftMargin = writer.leftMargin();
    		pageInfo.bottomMargin = writer.bottomMargin();
    		pageInfo.rightMargin = writer.rightMargin();
    		pageInfo.pageFitWidth = pageWidth - rightMargin;
    		pageInfo.pageFitHeight = this.pageFitHeight;
    		pageInfo.onePageFitWidth = pageWidth - leftMargin - rightMargin;
    		pageInfo.onePageFitHeight = pageHeight - topMargin - bottomMargin;
    		
    		pageInfo.pageMaxFitHeight = this.pageMaxFitHeight;
    		pageInfo.pageWidthNum = this.pageWidthNum;
    		pageInfo.pageHeightNum = this.pageHeightNum;
    		pageInfo.pageHeightSplitNum = this.pageHeightSplitNum;
    		
    		pageInfo.pageSplitHeight = this.pageSplitHeight;
    		pageInfo.pageSplitHeightOffset = this.pageSplitHeightOffset;
    		
    		return pageInfo;
    	}

    	/**
    	 * 为水平计算新建一页
    	 * @return
    	 * @see #newPage
    	 */
    	public PicturePageInfo newPageForHorizontal(Integer pageNum) {
    		writer.newPage();
    		
    		PicturePageInfo pageInfo = createPageInfo();
    		pageInfo.pageWidthNum = this.pageWidthNum + 1;
			pageInfo.canvasMapping = new HashMap<Integer, PicturePageInfo>();
			pageInfo.canvasMapping.put(pageNum, pageInfo);
    		
    		return pageInfo;
    	}
    	
    	/**
    	 * 只增加页面参数, 不实际新建一页
    	 * @return
    	 */
    	public PicturePageInfo addNewPage() {
    		this.pageFitHeight += this.pageHeight;
    		this.pageMaxFitHeight = this.pageFitHeight;
    		++pageHeightNum;
    		++pageHeightSplitNum;
    		
    		PicturePageInfo pageInfo = createPageInfo();
    		
    		canvasMapping.put(Integer.valueOf(pageInfo.pageHeightNum), pageInfo);
    		
    		return pageInfo;
    	}
    	
    	public float getActulPageMaxFitHeight() {
    		return pageMaxFitHeight - pageSplitHeightOffset;
    	}
    	
    	public float getActulPageFitHeight() {
    		return pageFitHeight - pageSplitHeightOffset;
    	}
    	
    	public float getActulOnePageFitHeight() {
    		return pageSplitHeight;
    	}
    	
	}
	
	//==================================================
	// Draw Strategy Classes
	//==================================================
	interface DrawStrategy {
		
		public void setReportExcelPictureTable(ReportExcelPictureTable reportExcelPictureTable);
		public int getDrawBodyWidth();
		public void setDrawBodyWidth(int drawBodyWidth);
		public int getDrawBodyHeight();
		public void setDrawBodyHeight(int drawBodyHeight);
		public int getDrawBodySplitHeight();
		public void setDrawBodySplitHeight(int i);
		
		public void calculate(PictureTableCellInfo info);
	}
	
	static abstract class AbstractDrawStrategy implements DrawStrategy {
		
		protected ReportExcelPictureTable exTable;
		protected int drawBodyWidth;
		protected int drawBodyHeight;
		protected int drawBodySplitHeight;
		protected PictureTableCellInfo info;
		
		public void calculate(PictureTableCellInfo info) {
			this.info = info;

			calculateCellsPos();
			
			// Date: 2017-06-16 - 浮动图片floating顶点对其行格左上角坐标
			calculateFloatingPos();
		}
		
		/**
		 * 计算每个格子的坐标
		 * @param splitHeight
		 */
		protected void calculateCellsPos() {
			calculateYPosAndSave();
			
			calculateCellsXYPos(info.title);
			calculateCellsXYPos(info.header);
			calculateCellsXYPos(info.detail);
			if (drawBodyHeight < 1) {
				calculateCellsXYPos(info.footer);

				addAllToCells(info.title);
				addAllToCells(info.header);
				addAllToCells(info.detail);
				addAllToCells(info.footer);
			} else {
			    calculateCellsPos(drawBodyHeight);
			}
		}
		
		/**
		 * 计算浮动对象左上角坐标. 根据最新的格子每行的坐标计算
		 */
		protected abstract void calculateFloatingPos();
		
		/**
		 * 计算每个格子的坐标
		 * @param splitHeight
		 */
		protected abstract void calculateCellsPos(int splitHeight);
		
		/**
		 * 处理一行格子
		 * @param rows 按行存放格子的集合
		 * @param row_ 当前处理的行的格子
		 * @param currentPos 当前处理的行的格子的首格坐标信息
		 * @param flag 处理需要的辅助信息
		 */
		protected abstract void processCellsForRow(List<List<PictureCell>> rows, List<PictureCell> row_, PictureCellPos currentPos, Flag flag, int type);
		
		/**
		 * 遍历处理格子的模板方法
		 * @param cells
		 * @param flag
		 */
		protected void processCells(List<PictureCell> cells, Flag flag, int type) {
			if (cells == null || cells.size() == 0) {
				return;
			}
			
			PictureCellPos currentPos = new PictureCellPos();
			PictureCell firstCell = cells.get(0);
			currentPos.cell = firstCell;
			currentPos.height = firstCell.maxHeight;
			currentPos.yPos = firstCell.yPos;

			List<PictureCell> row_ = new ArrayList<PictureCell>();
			List<List<PictureCell>> rows = new ArrayList<List<PictureCell>>();
			rows.add(row_);

			int mistake = flag.mistake;
			
			for (int cellIndex = 0, cellSize = cells.size(), currentRowIndex = firstCell.rowIndex; cellIndex < cellSize; cellIndex++) {
				PictureCell xcell = cells.get(cellIndex);
				if (currentRowIndex == xcell.rowIndex) {
					row_.add(xcell); // 找出按一行高度最大的格子为行高的一行全部格子, 并获得最大高度信息
					if (xcell.maxHeight > currentPos.height) {
						currentPos.cell = xcell;
						currentPos.height = xcell.maxHeight;
					}
				} else if (xcell.yPos + xcell.maxHeight < currentPos.yPos + currentPos.height + mistake) { // +1避免浮点误差
					row_.add(xcell);
				} else if (xcell.yPos < currentPos.yPos + currentPos.height - mistake) {
					row_.add(xcell);
					currentPos.height += (xcell.yPos + xcell.maxHeight - currentPos.yPos - currentPos.height);
				} else {
					
					processCellsForRow(rows, row_, currentPos, flag, type);
					
					flag.lastCell = row_.get(row_.size() - 1);
					
					currentRowIndex = xcell.rowIndex;
					--cellIndex;
					row_ = new ArrayList<PictureCell>();
					rows.add(row_);
					
					currentPos = new PictureCellPos();
					currentPos.cell = xcell;
					currentPos.height = xcell.maxHeight;
					currentPos.yPos = xcell.yPos;
				}
			}
			
			// 处理最后的格子
			if (row_.size() > 0) {
				processCellsForRow(rows, row_, currentPos, flag, type);
			}
			
			flag.lastCell = cells.get(cells.size() - 1);
			
		}
		
		/**
		 * 添加格子到要绘制的列表
		 * @param list
		 */
		protected void addAllToCells(List<PictureCell> list) {
			if (list != null && list.size() > 0) {
				info.cells.addAll(list);
			}
		}
		
		/**
		 * 计算每个格子的X坐标, 同时将之前#calculateYPosAndSave方法得到的Y坐标赋值给每个格子实例
		 * @param list
		 */
		protected void calculateCellsXYPos(List<PictureCell> list) {
			if (list == null || list.size() == 0) {
				return;
			}
			
			Map<Integer, PictureCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			Map<Integer, PictureCellPos> colIndexPosMapping = info.colIndexPosMapping;
			
			for (PictureCell cell : list) {
				Integer colkey = Integer.valueOf(cell.colIndex);
				PictureCellPos colValue = colIndexPosMapping.get(colkey);
				if (colValue != null) {
					cell.xPos = colValue.xPos;
				} else {
					float xPos = info.calculateXPos(cell);
					colValue = new PictureCellPos();
					colValue.xPos = xPos;
					colIndexPosMapping.put(colkey, colValue);
					cell.xPos = xPos;
				}
				
				Integer rowkey = Integer.valueOf(cell.rowIndex);
				PictureCellPos rowValue = rowIndexPosMapping.get(rowkey);
				cell.yPos = rowValue.yPos;
				
				int width = info.getCellWidth(cell.colIndex, cell.colspan);
				cell.maxWidth = width;
				
				cell.drawWidth = cell.maxWidth;
				cell.drawHeight = cell.maxHeight;
				
				cell.originalXPos = cell.xPos;
				cell.originalYPos = rowValue.yPos;
				cell.originalWidth = cell.maxWidth;
				cell.originalHeight = cell.maxHeight;
			}
		}
		
		/**
		 * 计算每行格子的Y坐标
		 */
		protected void calculateYPosAndSave() {
			Map<Integer, PictureCellPos> map = new LinkedHashMap<Integer, PictureCellPos>();
			
			calculateYPosAndSave(map, info.title);
			calculateYPosAndSave(map, info.header);
			calculateYPosAndSave(map, info.detail);
			calculateYPosAndSave(map, info.footer);
			
			float yPos = 0;
			for (Iterator<PictureCellPos> iter = map.values().iterator(); iter.hasNext(); ) {
				PictureCellPos pos = iter.next();
				
				if (pos.cell.rowIndex > 0) {
					PictureCellPos prePos = map.get(Integer.valueOf(pos.cell.rowIndex - 1));
					
					if (prePos == null) {
						for (int i = pos.cell.rowIndex - 2; i > -1; i--) {
							prePos = map.get(Integer.valueOf(i));
							if (prePos != null) {
								break;
							}
						}
						if (prePos == null) {
							throw new RuntimeException("cell information error");
						}
					}
					
					if (prePos.cell.rowspan == 1) {
						yPos = prePos.yPos + prePos.height;
					} else {
						yPos = prePos.yPos;
						int subRowIndex = prePos.cell.rowIndex;
						for (int i = 0, currentPosRowIndex = pos.cell.rowIndex; i < prePos.heightArray.length && subRowIndex < currentPosRowIndex; i++, subRowIndex++) {
    						yPos += prePos.heightArray[i];
					    }
					}
					
					pos.yPos = yPos;
				}
				
				PictureCellPos posValue = new PictureCellPos();
				posValue.cell = pos.cell;
				posValue.height = pos.height;
				posValue.yPos = yPos;
				posValue.heightArray = pos.heightArray;
				info.rowIndexPosMapping.put(Integer.valueOf(pos.cell.rowIndex), posValue);
			}
		}
		
		/**
		 * 计算每行最小高度的格子
		 * @param map
		 * @param list
		 */
		protected void calculateYPosAndSave(Map<Integer, PictureCellPos> map, List<PictureCell> list) {
			if (list == null || list.size() == 0) {
				return;
			}
			
			Map<Integer, PictureCellPos> multiRowspanMap = new HashMap<Integer, PictureCellPos>();
			
			int maxRowIndex = 0;
			
			for (PictureCell cell : list) {
				float cellHeight = cell.maxHeight;
				Integer key = Integer.valueOf(cell.rowIndex);
				PictureCellPos pos = map.get(key);
				if (pos == null) {
					pos = new PictureCellPos();
					pos.cell = cell;
					pos.height = cellHeight;
					map.put(key, pos);
					
					if (cell.rowspan > 1) {
						multiRowspanMap.put(key, pos);
					}
				} else if (pos.height > cellHeight) {
					pos.cell = cell;
					pos.height = cellHeight; // 获得一行最小高度
					
					if (cell.rowspan == 1) {
						multiRowspanMap.remove(key);
					}
				}
				
				if (cell.rowIndex > maxRowIndex) {
					maxRowIndex = cell.rowIndex;
				}
			}
			
			// 计算多行为最小行时每行的高度
			if (multiRowspanMap.size() > 0) {
				for (Iterator<PictureCellPos> iter = multiRowspanMap.values().iterator(); iter.hasNext(); ) {
					PictureCellPos pos = iter.next();
					
					if (pos.heightArray != null) {
						continue;
					}
					
					pos.heightArray = calculateOneRowHeight(map, pos, maxRowIndex);
				}
			}
		}
		
		private float[] calculateOneRowHeight(Map<Integer, PictureCellPos> map, PictureCellPos pos, int maxRowIndex) {
			if (pos.cell.rowspan == 1) {
				return new float[]{ pos.height };
			}
			
			if (pos.heightArray != null) {
				return pos.heightArray;
			}
			
			pos.heightArray = new float[pos.cell.rowspan];
			
			int height = 0;
			for (int i = 1, j = pos.cell.rowspan, startRowIndex = pos.cell.rowIndex; i < j; ) {
				PictureCellPos nextPos = map.get(Integer.valueOf(startRowIndex + i));
				
				if (nextPos == null) {
					if (startRowIndex + i > maxRowIndex) {
						break; // last cell
					}
					++i;
					continue;
				}
				
				if (nextPos.cell.rowspan == 1) {
					pos.heightArray[i] = nextPos.height;
					height += nextPos.height;
					++i;
				} else {
					float[] heightArray = calculateOneRowHeight(map, nextPos, maxRowIndex);
					for (int k = 0, l = i, limit = pos.heightArray.length; k < heightArray.length && l < limit; k++, l++) {
						pos.heightArray[l] = heightArray[k];
						height += heightArray[k];
					}
					i += heightArray.length;
				}
			}
			
			pos.heightArray[0] = pos.height - height;

			return pos.heightArray;
		}

		/**
		 * 计算列表中每个格子的高度
		 * @param list
		 * @return
		 */
		protected int calculateHeight(List<PictureCell> list, boolean useRowIndexPosMapping) {
			// 要求计算的格子的heightArray都已计算出来
			// @see #calculateYPosAndSave
			Map<Integer, PictureCellPos> rowIndexPosMapping = null;
			if (useRowIndexPosMapping) {
				rowIndexPosMapping = info.rowIndexPosMapping;
			} else {
				rowIndexPosMapping = new LinkedHashMap<Integer, PictureCellPos>();
				calculateYPosAndSave(rowIndexPosMapping, list);
			}
			
			int height = 0;
			int preRowIndex = -1;
			PictureCellPos prePos = null;
			for (PictureCell cell : list) {
				PictureCellPos pos = rowIndexPosMapping.get(Integer.valueOf(cell.rowIndex));
				
				if (pos != null) {
					if (cell.rowIndex > preRowIndex) {
						
						if (prePos == null) {
							prePos = pos;
							preRowIndex = cell.rowIndex;
						}
						
						int n = cell.rowIndex - preRowIndex;
						if (n > 1 && prePos.cell.rowspan > 1) {
							int maxSize = prePos.heightArray.length;
							n = n > maxSize ? maxSize : n;
							
							for (int i = 1; i < n; ++i) {
								height += prePos.heightArray[i];
							}
						}
						if (pos.cell.rowspan > 1) {
							height += pos.heightArray[0]; // 如果最后一个格子是合并多行的, 那0就是格子的总高度
						} else {
							height += pos.height;
						}
						
						preRowIndex = cell.rowIndex;
						prePos = pos;
					}
				}
			}
			
			return height;
		}
		
		/**
		 * 计算每个格子占用的rowspan
		 * @param list
		 * @return
		 */
		protected int calculateRowspan(List<PictureCell> list) {
			if (list == null || list.size() == 0) {
				return 0;
			}
			
			int result = 0;
			for (PictureCell cell : list) {
				if (cell.rowIndex > result) {
					result = cell.rowIndex;
				}
			}
			return result + 1;
		}
		
		/**
		 * @param reportExcelPictureTable the reportExcelPictureTable to set
		 */
		public void setReportExcelPictureTable(ReportExcelPictureTable reportExcelPictureTable) {
			this.exTable = reportExcelPictureTable;
		}
		
		/**
		 * @return the drawBodyWidth
		 */
		public int getDrawBodyWidth() {
			return drawBodyWidth;
		}

		/**
		 * @param drawBodyWidth the drawBodyWidth to set
		 */
		public void setDrawBodyWidth(int drawBodyWidth) {
			this.drawBodyWidth = drawBodyWidth;
		}

		/**
		 * @return the drawBodyHeight
		 */
		public int getDrawBodyHeight() {
			return drawBodyHeight;
		}

		/**
		 * @param drawBodyHeight the drawBodyHeight to set
		 */
		public void setDrawBodyHeight(int drawBodyHeight) {
			this.drawBodyHeight = drawBodyHeight;
		}

		/**
		 * @return the drawBodySplitHeight
		 */
		public int getDrawBodySplitHeight() {
			return drawBodySplitHeight;
		}
		
		/**
		 * @param drawBodyReturnHeight the drawBodySplitHeight to set
		 */
		public void setDrawBodySplitHeight(int drawBodySplitHeight) {
			this.drawBodySplitHeight = drawBodySplitHeight;
		}

		protected class Flag {
			float currentMaxHeight;
			float currentHeight;
			float offsetHeight;
			int offsetRowIndex;
			float splitHeight;
			float directSplitStartHeight;
			float contentSplitHeight;
			float contentSplitOffset;
			boolean hasHeader;
			int headerRowspan;
			int headerHeight;
			PictureCell lastCell;
			Map<Integer, PictureCellPos> rowIndexPosMapping = new HashMap<Integer, PictureCellPos>();
			Map<Integer, List<PictureCell>> pageNumHeaderMapping = new HashMap<Integer, List<PictureCell>>();
			Map<String, Object> attr = new HashMap<String, Object>();
			int mistake;
		}
	}
	
	/**
	 * <code>NoSplitStrategy</code>内容不切分策略.<br/>
	 */
	static class NoSplitStrategy extends AbstractDrawStrategy {

		private static final String TEXT_CONTROL = "tc";
		private static final String CELL_LIST = "cl";
		private static final int METHOD_TYPE_1 = 1; // #recalculateCellsForTextControl
		private static final int METHOD_TYPE_2 = 2; // #addRow
		
		private float cellMaxHeight;
		
		@Override
		protected void calculateFloatingPos() {
			List<PictureCell> floatingList = info.floating;
			if (floatingList == null || floatingList.size() == 0) {
				return;
			}
			
			Map<Integer, PictureCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			Map<Integer, PictureCellPos> colIndexPosMapping = info.colIndexPosMapping;
			for (int i = 0, j = floatingList.size(); i < j; ++i) {
				PictureCell floating = floatingList.get(i);
				
				PictureCellPos colPos = colIndexPosMapping.get(Integer.valueOf(floating.colIndex));
				if (colPos != null) {
					floating.xPos = colPos.xPos;
				}
				
				PictureCellPos rowPos = rowIndexPosMapping.get(Integer.valueOf(floating.rowIndex));
				if (rowPos != null) {
					floating.yPos = rowPos.yPos;
				}
			}
		}
		
		@Override
		protected void calculateCellsPos() {
			int splitHeight = Integer.MAX_VALUE;
			super.drawBodyHeight = splitHeight;
			super.drawBodySplitHeight = splitHeight;
			
			// Excel"设置单元格格式-对齐-文本控制"
			Flag flag = recalculateCellsForTextControl();
			if (flag.attr.get(TEXT_CONTROL) != null) {
    			info.rowIndexPosMapping = new HashMap<Integer, PictureCellPos>();
    			info.colIndexPosMapping = new HashMap<Integer, PictureCellPos>();
    			super.calculateCellsPos();
			} else {
				if (drawBodyHeight < 1) {
					addAllToCells(info.title);
					addAllToCells(info.header);
					addAllToCells(info.detail);
					addAllToCells(info.footer);
				} else {
				    calculateCellsPos(drawBodyHeight);
				}
			}
			
			// 页面最大高宽
			cellMaxHeight = flag.lastCell.yPos + flag.lastCell.maxHeight;
		}
		
		@Override
		protected void calculateCellsPos(int splitHeight) {
			if (splitHeight < 1) {
				return;
			}
			
			// 处理title最下面的边框和header最下面的边框
			//processCellsBorderForTHDF();

			Flag flag = new Flag();
			int contentSplitHeight = (drawBodySplitHeight > splitHeight || drawBodySplitHeight < 1 ? splitHeight : drawBodySplitHeight);
			flag.contentSplitHeight = contentSplitHeight;
			flag.contentSplitOffset = splitHeight - contentSplitHeight;
			flag.currentMaxHeight = contentSplitHeight;
			flag.currentHeight = 0;
			flag.offsetHeight = 0;
			flag.offsetRowIndex = 0;
			flag.splitHeight = splitHeight;
			flag.mistake = MISTAKE;
			
			List<PictureCell> picCells = info.cells;
			
			if (info.title != null && info.title.size() > 0) {
				calculateRow(info.title, flag);
				picCells.addAll(info.title);
				flag.currentHeight += calculateHeight(info.title, true);

				flag.lastCell = info.title.get(info.title.size() - 1);
			}

			if (info.header != null && info.header.size() > 0) {
				calculateRow(info.header, flag);
				picCells.addAll(info.header);
				flag.hasHeader = true;
				flag.headerHeight = calculateHeight(info.header, true);
				flag.headerRowspan = calculateRowspan(info.header) - calculateRowspan(info.title);
				
				flag.currentHeight += flag.headerHeight;
				
				flag.lastCell = info.header.get(info.header.size() - 1);
			}
			
			if (flag.currentMaxHeight <= flag.currentHeight) {
				int n = (int) (flag.currentHeight / flag.splitHeight) + 1;
				flag.currentMaxHeight = n * splitHeight - flag.contentSplitOffset;
			}
			
			List<PictureCell> detail = info.detail;
			if (detail != null && detail.size() > 0) {
				addRow(picCells, detail, flag);
			}
			
			if (info.footer != null && info.footer.size() > 0) {
				recalculateFooterYPos(picCells, flag);
			}
			
			// 更新info
			info.rowIndexPosMapping = flag.rowIndexPosMapping;
			if (flag.pageNumHeaderMapping != null && flag.pageNumHeaderMapping.size() > 0) {
				HeaderInfo headerInfo = new HeaderInfo();
				headerInfo.pageNumHeaderMapping = flag.pageNumHeaderMapping;
				headerInfo.headerRowspan = flag.headerRowspan;
				headerInfo.headerHeight = flag.headerHeight;
				info.headerInfo = headerInfo;
			}
			
		}
		
		@SuppressWarnings("unchecked")
		@Override
		protected void processCellsForRow(List<List<PictureCell>> rows, List<PictureCell> row_, PictureCellPos currentPos,
										  Flag flag, int type) {
			switch (type) {
				case METHOD_TYPE_1 : {
					processCellsBorder(row_, rows); // 后绘制的边框会挡住前面绘制的边框
					recalculateCellsForTextControl(row_, currentPos, flag);
					break;
				}
				case METHOD_TYPE_2 : {
					addRow((List<PictureCell>) flag.attr.get(CELL_LIST), row_, currentPos, flag);
					break;
				}
				default : {}
			}
		}
		
		private void recalculateFooterYPos(List<PictureCell> picCells, Flag flag) {
			float offsetHeight = flag.offsetHeight;
			
			flag.offsetHeight = 0;
			float newStartYPos = flag.currentHeight;
			int newRowIndex = flag.lastCell.rowIndex + flag.lastCell.rowspan;
			
			PictureCell firstCell = info.footer.get(0);
		    int rowIndexOffset = firstCell.rowIndex;
		    float yPosOffset = firstCell.yPos;
			
			for (PictureCell cell : info.footer) {
				Integer colkey = Integer.valueOf(cell.colIndex);
				PictureCellPos colValue = info.colIndexPosMapping.get(colkey);
				if (colValue != null) {
					cell.xPos = colValue.xPos;
				} else {
					float xPos = info.calculateXPos(cell);
					colValue = new PictureCellPos();
					colValue.xPos = xPos;
					info.colIndexPosMapping.put(colkey, colValue);
					cell.xPos = xPos;
				}
				
				cell.yPos = cell.yPos - yPosOffset + newStartYPos;
				
				int width = info.getCellWidth(cell.colIndex, cell.colspan);
				cell.maxWidth = width;
				
				cell.drawWidth = cell.maxWidth;
				cell.drawHeight = cell.maxHeight;
				
				Integer rowkey = Integer.valueOf(cell.rowIndex - rowIndexOffset + newRowIndex);
				PictureCellPos rowValue = info.rowIndexPosMapping.get(rowkey);
				if (rowValue == null) {
					rowValue = new PictureCellPos();
					rowValue.cell = cell;
					rowValue.height = cell.maxHeight;
					rowValue.yPos = cell.yPos;
					info.rowIndexPosMapping.put(rowkey, rowValue);
				} else if (rowValue.height > cell.maxHeight) {
					rowValue.height = cell.maxHeight; // 最小高度
				}
			}
			
			addRow(picCells, info.footer, flag);
			
			flag.offsetHeight = flag.offsetHeight + offsetHeight;
		}
		
		private int calculateRow(List<PictureCell> row, Flag flag) {
			Map<Integer, PictureCellPos> infoMap = info.rowIndexPosMapping;
			Map<Integer, PictureCellPos> flagMap = flag.rowIndexPosMapping;
			
			for (int i = 0, j = row.size(), k = -1; i < j; i++) {
				PictureCell cell = row.get(i);
				
				if (cell.rowIndex != k) {
					PictureCellPos cellPos = infoMap.get(Integer.valueOf(cell.rowIndex));
					cellPos.yPos = cell.yPos;
					flagMap.put(Integer.valueOf(cell.rowIndex), cellPos);
				}
			}
			
			return 0;
		}
		
		private void addRow(List<PictureCell> picCells, List<PictureCell> cells, Flag flag) {
			flag.attr.put(CELL_LIST, picCells);
			processCells(cells, flag, METHOD_TYPE_2);
		}
		
		private void addRow(List<PictureCell> picCells, 
		                    List<PictureCell> row_, 
		                    PictureCellPos currentPos, 
		                    Flag flag) {
			// 在计算前先计算偏移
			calculateRow(row_, flag);
			
			flag.currentHeight += currentPos.height;
			picCells.addAll(row_);
		}
		
		private void recalculateCellsForTextControl(List<PictureCell> row_, PictureCellPos currentPos, Flag flag) {
			ReportExcelPictureTable rept = this.exTable;
			
			Map<Integer, PictureCellPos> rowIndexPosMapping = info.rowIndexPosMapping;
			
			Map<Integer, List<PictureCell>> mergeCellsMap = new LinkedHashMap<Integer, List<PictureCell>>();
			List<PictureCell> rowIndexCells = new ArrayList<PictureCell>();
			float maxChangeHeight = 0;
			int preRowIndex = row_.get(0).rowIndex;
			boolean hasChange = false;
			
			for (int i = 0, j = row_.size(); i < j; ++i) {
				PictureCell xcell = row_.get(i);
				if (xcell.rowIndex == preRowIndex) {
					rowIndexCells.add(xcell);
					
					float cellOriginalHeight = xcell.maxHeight;
					
					rept.setTextDrawControl(xcell);
					
					float cellNewHeight = xcell.maxHeight;
					if (cellOriginalHeight < cellNewHeight) {
						xcell.maxHeight = cellOriginalHeight;
						float changeHeight = cellNewHeight - cellOriginalHeight;
						if (changeHeight > maxChangeHeight) {
							maxChangeHeight = changeHeight;
						}
					}
					
					if (xcell.rowspan > 1) {
						Integer key = Integer.valueOf(xcell.rowIndex);
						List<PictureCell> mergeCells = mergeCellsMap.get(key);
						if (mergeCells == null) {
							mergeCells = new ArrayList<PictureCell>();
							mergeCellsMap.put(key, mergeCells);
						}
						PictureCell tempCell = new PictureCell();
						tempCell.maxHeight = cellOriginalHeight;
						mergeCells.add(tempCell);
						mergeCells.add(xcell);
					}
				} else {
					if (maxChangeHeight > 0) {
						hasChange = true;
						changeCellHeight(rowIndexCells, mergeCellsMap, rowIndexPosMapping, maxChangeHeight);
					}
					
					--i;
					rowIndexCells = new ArrayList<PictureCell>();
					maxChangeHeight = 0;
					preRowIndex = xcell.rowIndex;
				}
			}
			
			// 处理最后的格子
			if (rowIndexCells.size() > 0) {
				if (maxChangeHeight > 0) {
    				hasChange = true;
    				changeCellHeight(rowIndexCells, mergeCellsMap, rowIndexPosMapping, maxChangeHeight);
				}
			}
			
			if (hasChange) {
				flag.attr.put(TEXT_CONTROL, Boolean.TRUE);
			}
		}
		
		private void changeCellHeight(List<PictureCell> rowIndexCells, 
		                              Map<Integer, List<PictureCell>> mergeCellsMap, 
		                              Map<Integer, PictureCellPos> rowIndexPosMapping, 
		                              float maxChangeHeight) {
			for (PictureCell cell : rowIndexCells) {
				cell.maxHeight = cell.maxHeight + maxChangeHeight;
			}
			
			// 前面合并单元格如果有包含此rowIndex的所有格子的大小也需要增加
			if (mergeCellsMap.size() > 0) {
				int currentRowIndex = rowIndexCells.get(0).rowIndex;
				PictureCellPos currentRowIndexPos = rowIndexPosMapping.get(Integer.valueOf(currentRowIndex));
				float lastPos = currentRowIndexPos.yPos + currentRowIndexPos.height;
				
				for (Iterator<Integer> iter = mergeCellsMap.keySet().iterator(); iter.hasNext(); ) {
					Integer key = iter.next();
					if (key.intValue() < currentRowIndex) {
						// 原始Y坐标和高度比较看是否包含
						PictureCellPos preRowIndexPos = rowIndexPosMapping.get(key);
						float prePos = preRowIndexPos.yPos;

						List<PictureCell> mergeCells = mergeCellsMap.get(key);
						for (int cellIndex = 0, mergeCellsSize = mergeCells.size(); cellIndex < mergeCellsSize; cellIndex += 2) {
							PictureCell tempCell = mergeCells.get(cellIndex);
							float h = tempCell.maxHeight;
							if (lastPos < prePos + h + 1) { // 可以增加判断colIndex在左边的条件
								PictureCell mergeCell = mergeCells.get(cellIndex + 1);
								mergeCell.maxHeight = h + maxChangeHeight;
							}
						}
					}
				}
			}
		}
		
		/**
		 * 从新计算每行格子的高度
		 * @see #calculateCellsPos(float splitHeight)
		 * @see #addRow
		 */
		private Flag recalculateCellsForTextControl() {
			calculateYPosAndSave();
			
			calculateCellsXYPos(info.title);
			calculateCellsXYPos(info.header);
			calculateCellsXYPos(info.detail);
			calculateCellsXYPos(info.footer);
			
			Flag flag = new Flag();
			flag.attr.put(TEXT_CONTROL, null);
			flag.mistake = MISTAKE;
			recalculateCellsForTextControl(info.title, flag);
			recalculateCellsForTextControl(info.header, flag);
			recalculateCellsForTextControl(info.detail, flag);
			recalculateCellsForTextControl(info.footer, flag);

			return flag;
		}
		
		private void recalculateCellsForTextControl(List<PictureCell> cells, Flag flag) {
			if (cells == null || cells.size() == 0) {
				return;
			}
			processCells(cells, flag, METHOD_TYPE_1);
		}
		
		// 特别处理双线边框
		private void processCellsBorder(List<PictureCell> currentRow, List<List<PictureCell>> rows) {

			for (int i = 0, j = currentRow.size(), k = rows.size(); i < j; ++i) {
				PictureCell xcell = currentRow.get(i);
				
				if (xcell.colIndex > 0 && xcell.leftBorderWidth > 0) {
					int startIndex = i - 1;
					boolean flag = startIndex > -1 ? updateLeftCellBorder(xcell, startIndex, -1, currentRow) : false;
					
					if (!flag || xcell.rowspan > 1) {
						startIndex = j - 1;
						flag = startIndex > 1 ? updateLeftCellBorder(xcell, startIndex, i, currentRow) : false;
					}
				}
				
				if (xcell.rowIndex > 0 && xcell.topBorderWidth > 0) {
					
					int startIndex = i - 1;
					boolean flag = startIndex > -1 ? updateTopCellBorder(xcell, startIndex, currentRow) : false;
					
					if (!flag && k > 1) {
						List<PictureCell> preRow = rows.get(rows.size() - 2);
						
						updateTopCellBorder(xcell, preRow.size() - 1, preRow);
					}
					
				} // end if top
			}
				
		}
		
		private boolean updateLeftCellBorder(PictureCell xcell, int startIndex, int endIndex, List<PictureCell> currentRow) {
			
			boolean flag = false;
			
			// 处理合并单元格
			int rowspan = xcell.rowspan;
			int leftXPos = (int) (xcell.originalXPos + 0.1f);
			for (int preIndex = startIndex, rowIndex = xcell.rowIndex, rowspanIndex = xcell.rowIndex + rowspan - 1; preIndex > endIndex; --preIndex) {
				PictureCell leftCell = currentRow.get(preIndex);
				
				if (leftCell.colIndex >= xcell.colIndex) {
					continue;
				}
				
				int rightXPos = (int) (leftCell.originalXPos + leftCell.originalWidth + 0.1f);
				if (rightXPos == leftXPos) {
					if ( (rowIndex >= leftCell.rowIndex && rowIndex <= leftCell.rowIndex + leftCell.rowspan - 1)
							|| (rowIndex <= leftCell.rowIndex && leftCell.rowIndex <= rowspanIndex) ) {
						
						flag = true; // found
						
						BorderRoundRectangle leftCellBorder = leftCell.borderRoundRectangle;

						if (leftCellBorder != null) {

							BorderRoundRectangle currentCellBorder = xcell.borderRoundRectangle;
        					if (currentCellBorder.leftBorder instanceof DoubleBorderStyle) {
        						
        						DoubleBorderStyle currentCellDoubleBorder = (DoubleBorderStyle) currentCellBorder.leftBorder;
        						if (leftCellBorder.topBorder instanceof DoubleBorderStyle) {
        							if (rowIndex == leftCell.rowIndex) {
        								currentCellDoubleBorder.drawOutTopOrLeft = false;
        							}
        							
        							if (leftCellBorder.rightBorder instanceof DoubleBorderStyle) {
            							DoubleBorderStyle leftCellDoubleBorder = (DoubleBorderStyle) leftCellBorder.topBorder;
            							leftCellDoubleBorder.drawOutBottomOrRight = true;
            							leftCellDoubleBorder.drawInnerBottomOrRight = true;
        							}
        						}
        						
        						if (leftCellBorder.bottomBorder instanceof DoubleBorderStyle) {
        							if (rowspanIndex == leftCell.rowIndex + leftCell.rowspan - 1) {
        								PictureBorderStyle bottomBorder = currentCellBorder.bottomBorder;
        								if (bottomBorder instanceof DoubleBorderStyle && !bottomBorder.isDisabled) {
        									currentCellDoubleBorder.drawOutBottomOrRight = false;
        								}
        							}
        							
        							if (leftCellBorder.rightBorder instanceof DoubleBorderStyle) {
        								DoubleBorderStyle leftCellDoubleBorder = (DoubleBorderStyle) leftCellBorder.bottomBorder;
        								leftCellDoubleBorder.drawOutBottomOrRight = true;
        								leftCellDoubleBorder.drawInnerBottomOrRight = true;
        							}
        						}
        					}
        					
        					// 只处理双线
        					if (leftCellBorder.rightBorder instanceof DoubleBorderStyle) {
        						leftCellBorder.rightBorder.isDisabled = true;
        					}
        					
        					// 如果左右两个格子拼接处是同种类型的边框, 去掉一边边框, 从而使得边框叠加绘制效果更好. Excel多行单元格拼接比其行数少的单元格时按少行的边框为准
        					if (leftCellBorder.rightBorder != null) {
            					if (leftCell.rowIndex == xcell.rowIndex 
            							&& leftCell.rowspan >= xcell.rowspan 
            							&& leftCellBorder.rightBorder.getClass() == currentCellBorder.leftBorder.getClass()) {
            						leftCellBorder.rightBorder.isDisabled = true;
            					}
        					}
						}
    					
    					rowspan -= leftCell.rowspan;
    					if (rowspan < 1) {
    						break;
    					}
					}
				}
			}
			
			return flag;
		}
		
		private boolean updateTopCellBorder(PictureCell xcell, int startIndex, List<PictureCell> currentRow) {
			
			boolean flag = false;
			
			// 处理合并单元格
			int colspan = xcell.colspan;
			
			int topYPos = (int) (xcell.originalYPos + 0.1f);
			for (int preIndex = startIndex, colIndex = xcell.colIndex, colspanIndex = xcell.colIndex + colspan - 1; preIndex > -1; --preIndex) {
				PictureCell topCell = currentRow.get(preIndex);
				
				if (topCell.rowIndex >= xcell.rowIndex || topCell.colIndex > colspanIndex) {
					continue;
				}
				
				int bottomYPos = (int) (topCell.originalYPos + topCell.originalHeight + 0.1f);
				if (bottomYPos == topYPos) {
				
					if ( (colIndex >= topCell.colIndex && colIndex <= topCell.colIndex + topCell.colspan - 1)
						|| (colIndex <= topCell.colIndex && topCell.colIndex <= colspanIndex) ) {

						flag = true; // found
						
						// 双线边框特殊处理
						BorderRoundRectangle topCellBorder = topCell.borderRoundRectangle;
						if (topCellBorder != null) {
							
    						BorderRoundRectangle currentCellBorder = (BorderRoundRectangle) xcell.borderRoundRectangle;
    						if (currentCellBorder.topBorder instanceof DoubleBorderStyle) {
    							DoubleBorderStyle currentCellDoubleBorder = (DoubleBorderStyle) currentCellBorder.topBorder;
    							
    							if (topCellBorder.leftBorder instanceof DoubleBorderStyle) {
    								if (colIndex == topCell.colIndex) {
    									currentCellDoubleBorder.drawOutTopOrLeft = false; // 如果双线边上下对齐, 则去掉下边双线上外线的缝合部分
    								}
    								
    								if (topCellBorder.bottomBorder instanceof DoubleBorderStyle) {
        								DoubleBorderStyle topCellDoubleBorder = (DoubleBorderStyle) topCellBorder.leftBorder;
        								topCellDoubleBorder.drawInnerBottomOrRight = true;
        								topCellDoubleBorder.drawOutBottomOrRight = true;
    								}
    							}
    							
    							if (topCellBorder.rightBorder instanceof DoubleBorderStyle) {
    								if (colspanIndex == topCell.colIndex + topCell.colspan - 1) {
    									PictureBorderStyle rightBorder = currentCellBorder.rightBorder;
    									if (rightBorder instanceof DoubleBorderStyle && !rightBorder.isDisabled) {
    										currentCellDoubleBorder.drawOutBottomOrRight = false;
    									}
    								}
    								
    								if (topCellBorder.bottomBorder instanceof DoubleBorderStyle) {
        								DoubleBorderStyle topCellDoubleBorder = (DoubleBorderStyle) topCellBorder.rightBorder;
        								topCellDoubleBorder.drawInnerBottomOrRight = true;
        								topCellDoubleBorder.drawOutBottomOrRight = true;
    								}
    							}
    						}
    						
    						// 只处理双线
    						if (topCellBorder.bottomBorder instanceof DoubleBorderStyle) {
    							topCellBorder.bottomBorder.isDisabled = true;
    						}
    						
        					// 如果上下两个格子拼接处是同种类型的边框, 去掉一边边框, 从而使得边框叠加绘制效果更好
    						if (topCellBorder.bottomBorder != null) {
            					if (topCell.colIndex >= xcell.colIndex 
            							&& topCellBorder.bottomBorder.getClass() == currentCellBorder.topBorder.getClass()) {
            						topCellBorder.bottomBorder.isDisabled = true;
            					}
    						}
						}
						
						colspan -= topCell.colspan;
						if (colspan < 1) {
							break;
						}
					}
				}
			}
			
			return flag;
		}
		
	}
	
	//==================================================
	// Picture Border Style Classes
	//==================================================
	static class BorderRoundRectangle {
		PictureBorderStyle topBorder;
		PictureBorderStyle leftBorder;
		PictureBorderStyle bottomBorder;
		PictureBorderStyle rightBorder;
		protected Object other;
		
		public void cellLayout(PictureCell cell, DrawRectangle position, Graphics2D g2d) {
			
			DrawRectangle rect = (DrawRectangle) position;

			// Excel是上下边框短, 双线间内容是透明会被背景色填充. 按上下左右绘制, PDF绘制边框是从坐标点向外画
			float xStart = position.getLeft();
			float yStart = position.getTop();
			float xEnd = position.getRight();
			float yEnd = position.getBottom();
			if (topBorder != null && (!topBorder.isDisabled || rect.drawBorderForceTop)) {
				float xStart0 = xStart;
				float xEnd0 = xEnd;
				if (leftBorder != null && (!leftBorder.isDisabled || rect.drawBorderForceLeft)) {
					xStart0 += leftBorder.width;
				}
				if (rightBorder != null && (!rightBorder.isDisabled || rect.drawBorderForceRight)) {
					xEnd0 -= rightBorder.width;
				}
				topBorder.render(g2d, position, xStart0, yStart, xEnd0, yStart,
				                 DrawRectangle.TOP, other == null ? this : other);
			}

			if (bottomBorder != null && (!bottomBorder.isDisabled || rect.drawBorderForceBottom)) {
				float xStart0 = xStart;
				float xEnd0 = xEnd;
				if (leftBorder != null && (!leftBorder.isDisabled || rect.drawBorderForceLeft)) {
					xStart0 += leftBorder.width;
				}
				if (rightBorder != null && (!rightBorder.isDisabled || rect.drawBorderForceRight)) {
					xEnd0 -= rightBorder.width;
				}
				bottomBorder.render(g2d, position, xStart0, yEnd, xEnd0, yEnd, 
				                    DrawRectangle.BOTTOM, other == null ? this : other);
			}
			
			if (leftBorder != null && (!leftBorder.isDisabled || rect.drawBorderForceLeft)) {
				leftBorder.render(g2d, position, xStart, yStart, xStart, yEnd,
				                  DrawRectangle.LEFT, other == null ? this : other);
			}

			if (rightBorder != null && (!rightBorder.isDisabled || rect.drawBorderForceRight)) {
				rightBorder.render(g2d, position, xEnd, yStart, xEnd, yEnd, 
				                   DrawRectangle.RIGHT, other == null ? this : other);
			}
		}
		
		public static BorderRoundRectangle copyBorder(BorderRoundRectangle borderRoundRectangle) {
			if (borderRoundRectangle == null) {
				return null;
			}
			BorderRoundRectangle newBorderRoundRectangle = new BorderRoundRectangle();
			newBorderRoundRectangle.topBorder = copyBorder(borderRoundRectangle.topBorder);
			newBorderRoundRectangle.leftBorder = copyBorder(borderRoundRectangle.leftBorder);
			newBorderRoundRectangle.bottomBorder = copyBorder(borderRoundRectangle.bottomBorder);
			newBorderRoundRectangle.rightBorder = copyBorder(borderRoundRectangle.rightBorder);
			return newBorderRoundRectangle;
		}

		public static PictureBorderStyle copyBorder(PictureBorderStyle borderStyle) {
			return borderStyle == null ? null : borderStyle.copyBorderStyle();
		}
	}
	
	static abstract class PictureBorderStyle {
		/**
		 * 边框宽度(px)
		 */
		float width;
		/**
		 * 边框颜色
		 */
		Color color;
		/**
		 * 禁止绘制
		 */
		boolean isDisabled;

		public PictureBorderStyle(float width, Color color) {
			this.width = width;
			this.color = color;
		}
		
		public float getBorderWidth() {
			return width;
		}
		
		public abstract PictureBorderStyle copyBorderStyle();
		
		/**
		 * 渲染边框
		 * @param canvas java.awt.Graphics2D
		 * @param position
		 * @param startX
		 * @param startY
		 * @param endX
		 * @param endY
		 */
		public abstract void render(Graphics2D canvas, DrawRectangle position, float startX, float startY, float endX, float endY, int side, Object o);
	}
	
	// NONE - HTML:none, borderWith:NONE(1)
	
	// Line - HTML:solid, borderWidth:THIN(1),MEDIUM(2),THICK(3)
	static class LineBorderStyle extends PictureBorderStyle {
		
		public LineBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PictureBorderStyle copyBorderStyle() {
			PictureBorderStyle result =  new LineBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			return result;
		}

		public void render(Graphics2D canvas, DrawRectangle position, float startX, float startY, float endX, float endY, int side,
						   Object o) {
			Stroke originalStroke = canvas.getStroke();
			Color originalColor = canvas.getColor();
			
			canvas.setStroke(new BasicStroke(width));
			canvas.setColor(color);
			canvas.draw(new Line2D.Float(startX, startY, endX, endY));
			
			canvas.setStroke(originalStroke);
			canvas.setColor(originalColor);
		}
	}
	
	// Double - HTML:double, borderWidth:DOUBLE(1)
	static class DoubleBorderStyle extends PictureBorderStyle {
		
		private static final float OFFSET = 2;//1.5f;//2f;
		
		protected boolean drawOutTopOrLeft = true;
		protected boolean drawOutBottomOrRight = true;
		protected boolean drawInnerTopOrLeft = false;
		protected boolean drawInnerBottomOrRight = false;

		public DoubleBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PictureBorderStyle copyBorderStyle() {
			DoubleBorderStyle result =  new DoubleBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			result.drawOutTopOrLeft = this.drawOutTopOrLeft;
			result.drawOutBottomOrRight = this.drawOutBottomOrRight;
			result.drawInnerTopOrLeft = this.drawInnerTopOrLeft;
			result.drawInnerBottomOrRight = this.drawInnerBottomOrRight;
			return result;
		}

		public void render(Graphics2D canvas, DrawRectangle position, float startX, float startY, float endX, float endY, int side,
						   Object o) {
			BorderRoundRectangle borderRoundRectangle = null;
			if (o instanceof BorderRoundRectangle) {
				borderRoundRectangle = (BorderRoundRectangle) o;
			}
			
			// 注意绘制方向与PDF相反. 修改所有Y坐标
			float offset = OFFSET;
			float posOffset = 0.75f;
			float newStartX = startX;
			float newStartY = startY;
			float newEndX = endX;
			float newEndY = endY;
			switch (side) {
				case 1: {
					// top
					newStartY += offset;
					newEndY += offset;
					
					if (borderRoundRectangle != null) {
	    				if (borderRoundRectangle.leftBorder != null && borderRoundRectangle.leftBorder instanceof DoubleBorderStyle) {
	    					newStartX += (borderRoundRectangle.leftBorder.getBorderWidth() - posOffset);
	    				}
	    				if (borderRoundRectangle.rightBorder != null && borderRoundRectangle.rightBorder instanceof DoubleBorderStyle) {
	    					newEndX -= (borderRoundRectangle.rightBorder.getBorderWidth() - posOffset);
	    				}
					}
					
					break;
				}
				case 8: {
					// right
					newStartX -= offset;
					newEndX -= offset;
					
					if (borderRoundRectangle != null) {
	    				if (borderRoundRectangle.topBorder != null && borderRoundRectangle.topBorder instanceof DoubleBorderStyle) {
	    					newStartY += (borderRoundRectangle.topBorder.getBorderWidth() - posOffset);
	    				}
	    				if (borderRoundRectangle.bottomBorder != null && borderRoundRectangle.bottomBorder instanceof DoubleBorderStyle) {
	    					newEndY -= (borderRoundRectangle.bottomBorder.getBorderWidth() - posOffset);
	    				}
					}
					
					break;
				}
				case 2: {
					// bottom
					newStartY -= offset;
					newEndY -= offset;
					
					if (borderRoundRectangle != null) {
	    				if (borderRoundRectangle.leftBorder != null && borderRoundRectangle.leftBorder instanceof DoubleBorderStyle) {
	    					newStartX += (borderRoundRectangle.leftBorder.getBorderWidth() - posOffset);
	    				}
	    				if (borderRoundRectangle.rightBorder != null && borderRoundRectangle.rightBorder instanceof DoubleBorderStyle) {
	    					newEndX -= (borderRoundRectangle.rightBorder.getBorderWidth() - posOffset);
	    				}
					}
					
					break;
				}
				case 4: {
					// left
					newStartX += offset;
					newEndX += offset;
					
					if (borderRoundRectangle != null) {
	    				if (borderRoundRectangle.topBorder != null && borderRoundRectangle.topBorder instanceof DoubleBorderStyle) {
	    					newStartY += (borderRoundRectangle.topBorder.getBorderWidth() - posOffset);
	    				}
	    				if (borderRoundRectangle.bottomBorder != null && borderRoundRectangle.bottomBorder instanceof DoubleBorderStyle) {
	    					newEndY -= (borderRoundRectangle.bottomBorder.getBorderWidth() - posOffset);
	    				}
					}
					
					break;
				}
				default:
					return;
			}

			Stroke originalStroke = canvas.getStroke();
			Color originalColor = canvas.getColor();
			
			canvas.setStroke(new BasicStroke(width));
			canvas.setColor(color);

			if (drawOutTopOrLeft && drawOutBottomOrRight) {
				canvas.draw(new Line2D.Float(startX, startY, endX, endY));
			} else {
				float x = this.getBorderWidth() - posOffset;
				float width = this.width;
				float startXOffset = 0;
				float startYOffset = 0;
				float endXOffset = 0;
				float endYOffset = 0;
				
				if (!drawOutTopOrLeft) {
					startXOffset = x;
					startYOffset = x;
				}
				if (!drawOutBottomOrRight) {
					endXOffset = x;
					endYOffset = x;
				}
				
				switch (side) {
					case 1 :
					case 2 : {
						// top/bottom
						canvas.draw(new Line2D.Float(startX + startXOffset - width, startY, endX - endXOffset, endY));
						break;
					}
					case 4 :
					case 8 : {
						// left/right
						canvas.draw(new Line2D.Float(startX, startY + startYOffset, endX, endY - endYOffset));
						break;
					}
					default : {
						break;
					}
				}
			}
			
			if (!drawInnerTopOrLeft && !drawInnerBottomOrRight) {
				canvas.draw(new Line2D.Float(newStartX, newStartY, newEndX, newEndY));
			} else {
				float x = this.getBorderWidth() - posOffset;
				float width = this.width;
				float startXOffset = 0;
				float startYOffset = 0;
				float endXOffset = 0;
				float endYOffset = 0;
				
				if (drawInnerTopOrLeft) {
					startXOffset = x;
					startYOffset = x;
				}
				if (drawInnerBottomOrRight) {
					endXOffset = x;
					endYOffset = x;
				}
				
				switch (side) {
					case 1 :
					case 2 : {
						// top/bottom
						canvas.draw(new Line2D.Float(newStartX - startXOffset - width, newStartY, newEndX + endXOffset, newEndY));
						break;
					}
					case 4 :
					case 8 : {
						// left/right
						canvas.draw(new Line2D.Float(newStartX, newStartY - startYOffset, newEndX, newEndY + endYOffset));
						break;
					}
					default : {
						break;
					}
				}
			}
			
			canvas.setStroke(originalStroke);
			canvas.setColor(originalColor);
		}
		
		@Override
		public float getBorderWidth() {
			return width * 2 + OFFSET;
		}
	}
	
	// Dashed - HTML:dashed, borderWidth:DASHED(1),MEDIUM_DASHED(2)
	// Not contains DASH_DOT(1),MEDIUM_DASH_DOT(2),DASH_DOT_DOT(1),MEDIUM_DASH_DOT_DOTC(2),SLANTED_DASH_DOT(2)
	static class DashedBorderStyle extends PictureBorderStyle {
		
		private static final float[] dash = { 1.1f + 6 };
		
		public DashedBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PictureBorderStyle copyBorderStyle() {
			PictureBorderStyle result =  new DashedBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			return result;
		}

		public void render(Graphics2D canvas, DrawRectangle position, float startX, float startY, float endX, float endY, int side,
						   Object o) {
			Stroke originalStroke = canvas.getStroke();
			Color originalColor = canvas.getColor();
			
			canvas.setStroke(new BasicStroke(width, BasicStroke.CAP_BUTT,
			                             BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f));
			canvas.setColor(color);
			canvas.draw(new Line2D.Float(startX, startY, endX, endY));
            
			canvas.setStroke(originalStroke);
			canvas.setColor(originalColor);
		}
	}
	
	// Dotted - HTML:dotted, borderWidth:DOTTED(1),HAIR(1)
	static class DottedBorderStyle extends PictureBorderStyle {

		private static final float[] dash = { 0, 6, 0, 6 };
		
		public DottedBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PictureBorderStyle copyBorderStyle() {
			PictureBorderStyle result =  new DottedBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			return result;
		}

		public void render(Graphics2D canvas, DrawRectangle position, float startX, float startY, float endX, float endY, int side,
						   Object o) {
			Stroke originalStroke = canvas.getStroke();
			Color originalColor = canvas.getColor();
			
			canvas.setStroke(new BasicStroke(width, BasicStroke.CAP_ROUND,
                            BasicStroke.JOIN_ROUND, 0, dash, 0));
			canvas.setColor(color);
			canvas.draw(new Line2D.Float(startX, startY, endX, endY));
            
			canvas.setStroke(originalStroke);
			canvas.setColor(originalColor);
		}
	}
	
	// Dash_Dot - HTML:dashed, borderWidth:DASH_DOT(1),MEDIUM_DASH_DOT(2),DASH_DOT_DOT(1),MEDIUM_DASH_DOT_DOTC(2),SLANTED_DASH_DOT(2)
	static class DashDotBorderStyle extends PictureBorderStyle {

		public DashDotBorderStyle(float width, java.awt.Color color) {
			super(width, color);
		}
		
		public PictureBorderStyle copyBorderStyle() {
			PictureBorderStyle result =  new DashDotBorderStyle(width, color);
			result.isDisabled = this.isDisabled;
			return result;
		}

		public void render(Graphics2D canvas, DrawRectangle position, float startX, float startY, float endX, float endY, int side,
						   Object o) {
			Stroke originalStroke = canvas.getStroke();
			Color originalColor = canvas.getColor();
			
			canvas.setStroke(new BasicStroke(width));
			canvas.setColor(color);
			
			int dotWidth = 1;
			int dashWidth = 4;
			int dotWidthSpace = 5;
			int dashWidthSpace = 8;
			
			if ( ((int)startX) == ((int)endX) ) {
				// left/right
				float top = startY > endY ? endY : startY;
				float bottom = startY > endY ? startY : endY;
				final float X = startX;
				int height = (int) position.drawHeight;
	            for (int i = 0, j = 0; j < height; i++) {
	            	if (i % 2 == 0) {
	            		float y = bottom - dotWidth;
	            		canvas.draw(new Line2D.Float(X, bottom, X, y < top ? top : y));
	            		
	            		if (y < top) {
	            			break;
	            		}
	            		bottom -= dotWidthSpace;
	            		j += dotWidthSpace;
	            	} else {
	            		float y = bottom - dashWidth;
	            		canvas.draw(new Line2D.Float(X, bottom, X, y < top ? top : y));
	            		
	            		if (y < top) {
	            			break;
	            		}
	            		bottom -= dashWidthSpace;
	            		j += dashWidthSpace;
	            	}
	            }
				
			} else if ( ((int)startY) == ((int)endY) ){
				// top/bottom
				float left = startX > endX ? endX : startX;
	            float right = startX > endX ? startX : endX;
	            final float Y = endY;
	            int width = (int) position.drawWidth;
	            for (int i = 0, j = 0; j < width; i++) {
	            	if (i % 2 == 0) {
	            		float x = right - dotWidth;
	            		canvas.draw(new Line2D.Float(right, Y, x < left ? left : x, Y));
	            		
	            		if (x < left) {
	            			break;
	            		}
	            		right -= dotWidthSpace;
	            		j += dotWidthSpace;
	            	} else {
	            		float x = right - dashWidth;
	            		canvas.draw(new Line2D.Float(right, Y, x < left ? left : x, Y));
	            		
	            		if (x < left) {
	            			break;
	            		}
	            		right -= dashWidthSpace;
	            		j += dashWidthSpace;
	            	}
	            }
			}
			
			canvas.setStroke(originalStroke);
			canvas.setColor(originalColor);
		}
	}
	
	/**
	 * <code>PictureFloatingCollection</code>浮动对象操作类
	 */
	static class FloatingCollection {
		List<Floating> floatingList;
		int[] maxWH; // 浮动对象覆盖的范围
		
		public FloatingCollection() {
		}
		
		public void addFloating(List<PictureCell> cellList) {
			if (cellList != null && cellList.size() > 0) {
				if (floatingList == null) {
				    floatingList = new ArrayList<Floating>();
				}
				for (PictureCell cell : cellList) {
					Floating floating = new Floating(cell);
					floatingList.add(floating);
				}
			} 
		}
		
		/**
		 * 获得指定范围内的浮动对象的包含部分
		 * @param pageHeightNum
		 * @param pageWidthNum
		 * @param pageWidth
		 * @param pageHeight
		 * @return
		 */
		public List<Floating> getFloating(int pageHeightNum, int pageWidthNum, float pageWidth, float pageHeight) {
			if (floatingList == null 
					|| floatingList.size() == 0
					|| pageHeightNum < 1
					|| pageWidthNum < 1
					|| pageWidth < 0
					|| pageHeight < 0) {
				return null;
			}
			
			float lastStartXPos = (pageWidthNum - 1) * pageWidth + 0.1f;
			float lastStartYPos = (pageHeightNum - 1) * pageHeight + 0.1f;
			float lastEndXPos = pageWidthNum * pageWidth + 0.1f;
			float lastEndYPos = pageHeightNum * pageHeight + 0.1f;
			
			List<Floating> result = new ArrayList<Floating>();
			List<Floating> list = new ArrayList<Floating>();
			for (Floating floating : floatingList) {
				
				float startXPos = floating.startXPos;
				float startYPos = floating.startYPos;
				float endXPos = floating.endXPos;
				float endYPos = floating.endYPos;
				
				if (endXPos <= lastStartXPos 
						|| startXPos >= lastEndXPos
						|| endYPos <= lastStartYPos 
						|| startYPos >= lastEndYPos) {
					// 不包含在当前页
					list.add(floating);
				} else if (startXPos >= lastStartXPos 
						&& endXPos <= lastEndXPos
						&& startYPos >= lastStartYPos
						&& endYPos <= lastEndYPos) {
					// 完全包含在当前页
					result.add(floating);
					//floating.drawCount++;
				} else {
					// 部分包含
					boolean completed = false;
					
					if (startXPos >= lastStartXPos) {
						floating.drawXPos = startXPos;
						// 左边(整个左边/部分左边)
						if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) {
							floating.rect.drawWidth = lastEndXPos - startXPos;
						} else if (startYPos >= lastStartYPos) { // 上半部
							floating.rect.drawWidth = lastEndXPos - startXPos;
							floating.rect.drawHeight = lastEndYPos - startYPos;
						} else { // 下半部
							floating.drawYPos = lastStartYPos;
							floating.rect.drawWidth = lastEndXPos - startXPos;
							
							float height = floating.cell.maxHeight - (lastStartYPos - startYPos);
							if (height > pageHeight) {
								height = pageHeight;
							}
							floating.rect.drawHeight = height;
						}
						
					} else {
						// 右边(整个右边/部分右边)
						if (endXPos <= lastEndXPos) {
							
							if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) { // 整个
								completed = true; // 绘制完毕
								
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = endXPos - lastStartXPos;
								floating.rect.drawHeight = floating.cell.maxHeight;
							} else if (startYPos >= lastStartYPos) { // 上半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = endXPos - lastStartXPos;
								floating.rect.drawHeight = lastEndYPos - startYPos;
							} else { // 下半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = lastStartYPos;
								
								floating.rect.drawWidth = endXPos - lastStartXPos;
								
								float height = floating.cell.maxHeight - (lastStartYPos - startYPos);
								if (height > pageHeight) {
									height = pageHeight;
								} else {
									completed = true; // 绘制完毕
								}
								floating.rect.drawHeight = height;
							}
							
						} else {
							// 不是最左和最右边, 此为多列切分中间部分。类似右边处理, 只是不会绘制完毕
							if (endYPos <= lastEndYPos && startYPos >= lastStartYPos) { // 整个中间
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;  
								floating.rect.drawWidth = pageWidth;
								floating.rect.drawHeight = floating.cell.maxHeight;
							} else if (startYPos >= lastStartYPos) { // 中间上半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = startYPos;
								floating.rect.drawWidth = pageWidth;
								floating.rect.drawHeight = lastEndYPos - startYPos;
							} else {  // 中间下半部
								floating.drawXPos = lastStartXPos;
								floating.drawYPos = lastStartYPos;
								
								floating.rect.drawWidth = pageWidth;
								
								float height = floating.cell.maxHeight - (lastStartYPos - startYPos);
								if (height > pageHeight) {
									height = pageHeight;
								}
								floating.rect.drawHeight = height;
							}
						}
					} 
					
					result.add(floating);
					
					// 如果是最后部分, 则不需要再添加到list
					if (!completed) {
						list.add(floating);
					}
					
					//floating.drawCount++;
				}
			}

			if (floatingList.size() != list.size()) {
				floatingList.clear();
				for (int i = 0, j = list.size(); i < j; i++) {
					Floating floating = list.get(i);
					floatingList.add(floating);
				}
			}
			
			return result;
		}
		
		/**
		 * 获得浮动对象最大的宽高
		 * @param pageWidth
		 * @param pageHeight
		 * @return
		 */
		public int[] getFloatingMaxWH(float pageWidth, float pageHeight) {
			int[] result = new int[]{0, 0};
			if (!hasFloating()) {
				return result;
			}
			
			float w = 0;
			float h = 0;
			for (Floating floating : floatingList) {
				if (floating.endXPos > w) {
					w = floating.endXPos;
				}
				if (floating.endYPos > h) {
					h = floating.endYPos;
				}
			}

			int n = (int) (w / pageWidth);
			float m = remainderToFloat(w, pageWidth);
			if (m > 0) {
				++n;
			}
			w = n;
			
			n = (int) (h / pageHeight);
			m = remainderToFloat(h, pageHeight);
			if (m > 0) {
				++n;
			}
			h = n;
			
			result[0] = (int)w;
			result[1] = (int)h;
			
			return result;
		}
		
		public boolean hasFloating() {
			return floatingList != null && floatingList.size() > 0;
		}
	}
	
	/**
	 * <code>Floating</code>表示PictureF中指定位置的对象(浮动对象)
	 */
	static class Floating {

		PictureCell cell;
		float startXPos;
		float startYPos;
		float endXPos;
		float endYPos;
		float drawXPos;
		float drawYPos;
		DrawRectangle rect;
		
		public Floating(PictureCell cell) {
			// 注意:Picture绘制(0, 0)位于页面左上角
			this.cell = cell;
			float w = cell.maxWidth;
			float h = cell.maxHeight;
			startXPos = cell.xPos;
			startYPos = cell.yPos;
			endXPos = cell.xPos + w;
			endYPos = cell.yPos + h;
			
			this.drawXPos = startXPos;
			this.drawYPos = startYPos;
			
			this.rect = new DrawRectangle(cell.xPos, cell.yPos, w, h);
		}
	}
	
	/**
	 * <code>Position</code>Picture绘制位置
	 */
	static class Position implements net.coobird.thumbnailator.geometry.Position {
		private int xOffset;
		private int yOffset;
		
		public Position(int xOffset, int yOffset) {
			this.xOffset = xOffset;
			this.yOffset = yOffset;
		}
		
		public Point calculate(int enclosingWidth, int enclosingHeight,
			       				int width, int height, int insetLeft, int insetRight,
			       				int insetTop, int insetBottom) {
			int x = insetLeft + xOffset;
			int y = insetTop + yOffset;
			return new Point(x, y);
	    }
	}
	
	//==================================================
	// Picture Other Classes
	//==================================================
	/**
	 * <code>DrawRectangle</code>绘制区域
	 */
	static class DrawRectangle {
		float drawXPos;
		float drawYPos;
		float drawWidth;
		float drawHeight;
		float drawBorderWidthLeft;
		float drawBorderWidthRight;
		float drawBorderWidthTop;
		float drawBorderWidthBottom;
		boolean drawBorderForceLeft;
		boolean drawBorderForceRight;
		boolean drawBorderForceTop;
		boolean drawBorderForceBottom;
		
		int border;
		
		// CONSTANTS:

		/** This is the value that will be used as <VAR>undefined </VAR>. */
		public static final int UNDEFINED = -1;

		/** This represents one side of the border of the <CODE>Rectangle</CODE>. */
		public static final int TOP = 1;

		/** This represents one side of the border of the <CODE>Rectangle</CODE>. */
		public static final int BOTTOM = 2;

		/** This represents one side of the border of the <CODE>Rectangle</CODE>. */
		public static final int LEFT = 4;

		/** This represents one side of the border of the <CODE>Rectangle</CODE>. */
		public static final int RIGHT = 8;

		/** This represents a rectangle without borders. */
		public static final int NO_BORDER = 0;

		/** This represents a type of border. */
		public static final int BOX = TOP + BOTTOM + LEFT + RIGHT;

		public DrawRectangle(float drawXPos, float drawYPos, float drawWidth, float drawHeight) {
			this.drawXPos = drawXPos;
			this.drawYPos = drawYPos;
			this.drawWidth = drawWidth;
			this.drawHeight = drawHeight;
		}
		
		public void setBorder(final int border) {
			this.border = border;
		}
		
		public int getBorder() {
			return border;
		}
		
		public boolean hasBorder() {
			return border > 0;
		}
		
		public boolean hasBorder(final int type) {
			if (border == UNDEFINED)
				return false;
			return (border & type) == type;
		}
		
		public float getLeft() {
			return drawXPos; // llx
		}
		
		public float getBottom() {
			return  drawYPos + drawHeight; // lly
		}
		
		public float getRight() {
			return drawXPos + drawWidth; // urx
		}
		
		public float getTop() {
			return drawYPos; // ury;
		}
	}
	
	//==================================================
	// Enum
	//==================================================
	enum TextControlEnum {
		DIRECT, STRETCH, SHRINK, CLIP, AUTOSIZE;
	};
	
	// 对齐方式. JUSTIFY需要自己实现
	enum HVAlignment {
	    LEFT, CENTER, RIGHT,
	    TOP, MIDDLE, BOTTOM
	}
	
	// 支持的图片类型
	public static enum PictureType {
		PNG, JPEG, BMP, GIF
	}
	
	//==================================================
	// Static Field/Method
	//==================================================
	private static final float UNIT_ZOOM_FAC = 1.0f;
	private static final float UNIT_PX_TO_PT_FAC = 0.75f;
	private static final int UNIT_INCH_TO_PX_FAC = 96;
	private static final int MISTAKE = 1;
	private static final PictureFont DEFAULT_TIP_FONT = new PictureFont("Helvetica", 16, PictureFont.NORMAL, Color.RED);
	private static final String FONT_DEFAULT_CHINESE = "SimSun";  // 宋体SimSun, 华文宋体STSong
	
	/**
	 * 单位磅(pt)转换为像素(px)
	 * @param pt
	 * @return
	 */
	private static int ptToPx(float pt) {
		return Math.round(pt / UNIT_PX_TO_PT_FAC);
	}
	
	private static int round(float f) {
		int f0 = (int) f;
		if (f0 == f) {
			return f0;
		}
		return Math.round(f + 0.5f);
	}
	
	/**
	 * 取余数. 避免1460.4 % 486.80002 = 486.79996
	 * @param a
	 * @param b
	 * @return
	 */
	private static float remainderToFloat(double a, double b) {
		BigDecimal a0 = new BigDecimal(a);
		a0 = a0.setScale(2, BigDecimal.ROUND_HALF_UP);
		BigDecimal b0 = new BigDecimal(b);
		b0 = b0.setScale(2, BigDecimal.ROUND_HALF_UP);
		BigDecimal c0 = a0.remainder(b0);
		return c0.floatValue();
	}
	
	/**
	 * 处理浮点数. 2.504->2.50, 2.505->2.51
	 * @param a
	 * @param scale
	 * @return
	 */
	private static float toRoundHalfUpFloat(double a, int scale) {
		BigDecimal a0 = new BigDecimal(a);
		a0 = a0.setScale(scale, BigDecimal.ROUND_HALF_UP);
		return a0.floatValue();
	}
	
	/**
	 * 追加信息到页面
	 * @param writer
	 * @param tip
	 * @param ex
	 */
	private static void writeErrorTip(PictureWriter writer, String tip, Exception ex) {
		if (tip == null) {
			tip = "";
		}
		
		if (ex != null) {
			String msg = ex.getMessage();
			if (msg == null) {
				StringBuilder stringBuilder = new StringBuilder();
	            StackTraceElement[] trace = ex.getStackTrace();
	            for (int i=0; i < trace.length; i++) {
	            	stringBuilder.append("\n\tat " + trace[i]);
	            }
	            msg = stringBuilder.toString();
			}
			
			tip = tip  + "\nError Trace: \n" + ex.getClass().getName() + "\n" + msg;
		}
		
		PictureCell xcell = new PictureCell();
		xcell.cellText = tip;
		xcell.cellFont = DEFAULT_TIP_FONT;
		DrawRectangle rect = new DrawRectangle(writer.leftMargin(), writer.topMargin(), 
		                                       writer.pageWidth - writer.leftMargin() - writer.rightMargin(),
		                                       writer.pageHeight - writer.topMargin() - writer.bottomMargin());
		writer.writeTextForClipCell(xcell, rect); // selector 不使用
	}
	
	// private methods
	private static boolean isValidReport(Report report) {
		// report.isEmptyReport() 也要输出页眉页脚的一面空白页
		return !(report == null);
	}

//	private static byte[] copy(byte[] source) {
//
//		byte[] data = new byte[source.length];
//		System.arraycopy(source, 0, data, 0, source.length);
//		return data;
//	}
//
//	private static XSSFWorkbook toExcel(byte[] middle) throws Exception {
//
//		ByteArrayInputStream in = new ByteArrayInputStream(middle);
//		try {
//			XSSFWorkbook excel = new XSSFWorkbook(in);
//			return excel;
//		} finally {
//			in.close();
//		}
//	}

}
